0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Notion SDK v5.23.0のWebhook署名検証と10,000行制限回避ヘルパー

0
Posted at

はじめに

2026年7月8日、Notion公式のJavaScript SDK「@notionhq/client」の v5.23.0 がリリースされました。今回のアップデートの目玉は以下の3点です。

  1. Webhook署名検証ヘルパーの追加verifyWebhookSignature / signWebhookPayload
  2. 約10,000行の暗黙的な上限を回避する全行取得ヘルパーiterateAllDataSourceRows / collectAllDataSourceRows
  3. 非同期タスクAPIへの対応asyncTasks.retrieveallow_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-markdownallow_async 追加 新機能 Medium 任意
5 HTTP 529(service_overload)の自動リトライ対応 改善 Medium 不要(自動適用)
6 relevance検索ソートの型修正 バグ修正 Low 型エラーが出ていた場合のみ
7 start_cursornull を許容 改善 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_cursorstart_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 でバージョンを上げ、該当する箇所から順に新ヘルパーへの移行を進めることをおすすめします。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?