はじめに
2026年7月8日、Notion公式のJavaScript SDK「@notionhq/client」の v5.23.0 がリリースされました。今回のアップデートの目玉は以下の3点です。
-
Webhook署名検証ヘルパーの追加(
verifyWebhookSignature/signWebhookPayload) -
約10,000行の暗黙的な上限を回避する全行取得ヘルパー(
iterateAllDataSourceRows/collectAllDataSourceRows) -
非同期タスクAPIへの対応(
asyncTasks.retrieveとallow_asyncオプション)
特に注目すべきは2番目の「10,000行制限」です。これまで dataSources.query を通常のページネーションで回している場合、エラーも警告も出さずに一定行数で取得が止まるという危険な挙動がありました。大規模なデータベース(データソース)を扱っているプロダクトでは、気づかないうちにデータが欠落していた可能性があります。
本記事では、この v5.23.0 の変更点を「何が変わったか」「なぜ重要か」「どう対応すべきか」の観点で整理します。
📌 影響を受ける人
- Notion Webhook を受信して署名検証を自前実装している人
- 10,000行を超える可能性があるデータソースを
dataSources.queryでクエリしている人- ページ作成やMarkdown更新を大規模・バッチで行っている人
変更の全体像
今回追加された主要機能の関係性を図示します。
変更内容
v5.23.0 に含まれる主な変更を severity 順に整理しました。
| # | 変更内容 | 種別 | Severity | 対応要否 |
|---|---|---|---|---|
| 1 |
iterateAllDataSourceRows / collectAllDataSourceRows 追加 |
新機能 | High | ✅ 該当者は移行推奨 |
| 2 |
verifyWebhookSignature / signWebhookPayload 追加 |
新機能 | Medium | ✅ 該当者は移行推奨 |
| 3 |
asyncTasks.retrieve 追加(非同期タスクAPI) |
新機能 | Medium | 任意 |
| 4 |
createPage / update-page-markdown に allow_async 追加 |
新機能 | Medium | 任意 |
| 5 | HTTP 529(service_overload)の自動リトライ対応 | 改善 | Medium | 不要(自動適用) |
| 6 | relevance検索ソートの型修正 | バグ修正 | Low | 型エラーが出ていた場合のみ |
| 7 |
start_cursor に null を許容 |
改善 | Low | 不要(型改善のみ) |
| 8 | バージョンバンプ(npm公開) | アナウンス | Low | 不要 |
1. iterateAllDataSourceRows / collectAllDataSourceRows(PR #730, #731)
単一の dataSources.query は約10,000行に達すると静かに停止します。エラーにならないため、既存コードでは「全件取得できている」と誤認しやすい落とし穴でした。新ヘルパーは created_time でクエリをパーティション分割し、上限を超えてページングしながら重複排除も自動で行います。
また #731 では、ページだけでなく子データソース行も返す wiki データソースに対して、ウィンドウの前進処理が正しく行われない不具合も修正されています。
2. verifyWebhookSignature / signWebhookPayload(PR #722)
これまで開発者が自前で実装していたHMAC-SHA256による署名検証が、公式ヘルパーとして提供されました。Node 18+、Bun、Deno、Cloudflare Workers、Vercel Edge、ブラウザ環境でそのまま動作します。
⚠️ Breaking Changeではないが注意点
verifyWebhookSignatureには、JSONとして再シリアライズしたボディではなく、受信した「生の」リクエストボディとX-Notion-Signatureヘッダーをそのまま渡す必要があります。フレームワークがボディを自動パースしている場合、raw bodyを別途取得する設定が必要です。
3〜4. 非同期タスクAPI(PR #728, #729, #736)
createPage や Markdown 更新APIに allow_async オプションが追加され、大規模・低速な書き込みをブロッキングせず非同期タスクとして扱えるようになりました。返却されたタスクは client.asyncTasks.retrieve() で完了までポーリングします。
影響と対応
| 状況 | 推奨アクション |
|---|---|
| Webhookを受信している |
verifyWebhookSignature に移行し、自前のHMAC実装を撤去する |
| データソースが10,000行を超える可能性がある |
dataSources.query の手動ページネーションを iterateAllDataSourceRows / collectAllDataSourceRows に置き換える |
| relevanceソートで型エラーが出ていた | 呼び出しコードを修正後の型形状に合わせて調整する |
next_cursor を start_cursor に渡す際、null チェックをしていた |
不要になったnullチェックを削除可能(任意) |
| 大量ページ作成/更新を行っている |
allow_async + asyncTasks.retrieve の利用を検討する |
移行の判断フローは以下の通りです。
コード例
Webhook署名検証: Before/After
Before(自前実装)
import crypto from "crypto"
function verifySignature(rawBody, signature, secret) {
const expected = crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest("hex")
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature)
)
}
app.post("/webhook", (req, res) => {
const isValid = verifySignature(
req.rawBody,
req.headers["x-notion-signature"],
process.env.NOTION_WEBHOOK_SECRET
)
if (!isValid) return res.status(401).send("Invalid signature")
// ...処理
})
After(公式ヘルパー利用)
import { verifyWebhookSignature } from "@notionhq/client"
app.post("/webhook", (req, res) => {
const isValid = verifyWebhookSignature({
body: req.rawBody, // 生のリクエストボディを渡す
headers: req.headers,
secret: process.env.NOTION_WEBHOOK_SECRET,
})
if (!isValid) return res.status(401).send("Invalid signature")
// ...処理
})
大規模データソースの全行取得: Before/After
Before(手動ページネーション、10,000行超で取りこぼしの恐れ)
async function getAllRows(client, dataSourceId) {
const rows = []
let cursor = undefined
do {
const res = await client.dataSources.query({
data_source_id: dataSourceId,
start_cursor: cursor,
})
rows.push(...res.results)
cursor = res.next_cursor
} while (cursor) // 約10,000行で暗黙的に打ち切られる可能性がある
return rows
}
After(新ヘルパーで上限を回避)
import { collectAllDataSourceRows } from "@notionhq/client"
async function getAllRows(client, dataSourceId) {
// created_timeでパーティション分割し、重複排除も自動で行う
return await collectAllDataSourceRows(client, {
data_source_id: dataSourceId,
})
}
ストリーミング処理したい場合は iterateAllDataSourceRows を使うと、メモリに全件を溜め込まずに for await で1行ずつ処理できます。
import { iterateAllDataSourceRows } from "@notionhq/client"
for await (const row of iterateAllDataSourceRows(client, {
data_source_id: dataSourceId,
})) {
// 1行ずつ処理
}
まとめ
- Notion SDK v5.23.0 では、Webhook署名検証と大規模データソースの全行取得という、実運用で踏みやすい落とし穴に対する公式ヘルパーが追加されました。
- 特に
dataSources.queryの約10,000行での暗黙的な打ち切りは、エラーが出ないぶん気づきにくく、既存プロダクトで静かにデータが欠落している可能性があります。心当たりのある方は早めにiterateAllDataSourceRows/collectAllDataSourceRowsへの移行を検討してください。 - Webhookを受信している場合も、自前のHMAC実装を
verifyWebhookSignatureに置き換えることで、実装ミスによる検証漏れのリスクを減らせます。 - そのほか、HTTP 529の自動リトライや型の改善(relevanceソート、
start_cursorのnull許容)も含まれており、アップデート自体は基本的に後方互換性を保ったまま導入できます。
まずは npm update @notionhq/client でバージョンを上げ、該当する箇所から順に新ヘルパーへの移行を進めることをおすすめします。