Notion APIをバックエンドとして使っていると速度が出ずデータが欠落する理由
Notionデータベースの現実的な限界
Notionはインターフェースが便利だ。1人開発者がサーバーとデータベースを別々に構築する時間がない時、Notionをバックエンドとして使う理由がこれだ。しかし、プロダクションレベルでこの構造を維持すると、すぐに壁にぶぶる。Notion APIはすべてのインテグレーション・トークンに対して秒間3回のリクエスト制限をかける。この制限を超えると、429エラーや529エラーが発生する。
データが少し溜まるだけで問題が大きくなる。一度に取得できるデータは最大100件だ。ページネーション処理を自分で実装しなければならず、ネストされたプロパティ構造をパースするためにサーバーロジックが複雑化する。1つのページに入るプロパティデータのサイズは2.5メガバイトに制限されている。この限界を無視してサービスをデプロイすると、ユーザーからのリクエストが集中した際に画面がフリーズしたり、データが欠落したりする。
API呼び出しを減らすキャッシングと平坦化構造
外部APIの呼び出しを毎回待つわけにはいかない。キャッシング層を前面に置く必要がある。RedisやCloudflare KVに静的データをキャッシュし、バックグラウンドワーカーで定期的に更新することで、API呼び出し全体の80パーセントをローカルで処理できるようになる。応答速度は200ミリ秒以下に落ちる。
PythonバックエンドでNotionの複雑なプロパティを平坦化するパーサーも必要だ。Notionのレスポンスはタイプ別に深くネストされている。辞書を巡回しながらタイプフィールドを確認し、テキストは文字列に結合し、リレーショナルデータはIDの配列だけを抽出するパーサーを作成する。この前処理のプロセスを経てこそ、フロントエンド開発者がパースロジックなしでデータをすぐに使い始めることができる。
Webhookの同期エラーとデータ復旧ルーチン
Notionで行が変更されても、Webhookを受信してすぐにAPIを叩くと古いデータが返ってくる。インデックス処理が遅れるためだ。パケットが消失したり、データが狂ったりする原因となる。
この問題を解決するには、定期的なバックグラウンド復旧ルーチンが必要だ。Notion APIの更新日時フィルターを活用し、ローカルキャッシュとタイムスタンプを比較する。エラーが発生した場合は指数バックオフアルゴリズムで待機してから再試行する。最後の同期時点以降に変更されたレコードだけを選別して強制的にアップデートするルーチンを組み込んでおいてこそ、データの整合性が崩れない。
サーバーレスミドルウェアを通したトークン管理とセキュリティ
クライアントのブラウザにNotionシーク릿キーを直接持たせてリクエストを送信するコードは危険だ。トークンがそのまま露出され、CORSの問題も発生する。サーバーレスミドルウェアプロキシを中間に置かなければならない理由がここにある。
クライアントはサーバーレスミドルウェアへリクエストを送り、ミドルウェアがサーバーの環境変数に隠しておいたトークンを付与してNotion APIと通信する。マルチテナント環境であれば、アプリのユーザーIDとNotionページの所有者をマッピングする逆権限テーブルをミドルウェアに持たせる必要がある。この構造を作ってこそ、トークン流出事故を防ぎ、安全にデータを分離することができる。