【実務・中級編】NotionとVercel・Next.jsでヘッドレスCMSを構築する:APIのレートリミット対策とISR(Incremental Static Regeneration)による高速ブログ運用 – プロジェクト・ナレッジ管理活用バイブル

—

Notionを最強のヘッドレスCMSへ変貌させる:Next.js ISRとAPIレートリミットを制する極限のアーキテクチャ

「ドキュメント管理はNotionだが、ブログや製品ドキュメントの公開用CMSはContentfulやMicroCMSを別途導入する」……そんな二重管理による情報のサイロ化に、君は疲弊していないか?

真のエンジニアリング・エクセレンスとは、「情報の入力源(Single Source of Truth)」を一本化し、運用の摩擦をゼロにすることにある。Notionは、非エンジニアの編集者にとって最高のUIでありながら、エンジニアにとっては強力な構造化データベースだ。

しかし、NotionをヘッドレスCMSとして実戦投入するには、避けては通れない「3リクエスト/秒」というあまりに貧弱なAPIレートリミットの壁がある。本稿では、Next.jsのISR(Incremental Static Regeneration)を駆使し、この制約を突破して爆速のサイトパフォーマンスを実現する、現場直結の知見を叩き込む。

—

1. アーキテクチャの全貌:Notionを「書き込み専用」と割り切る

Notion APIをランタイム(ブラウザからのリクエスト時)に叩くのは素人の仕事だ。ユーザーがサイトを訪れるたびにAPIを呼び出せば、瞬時にレートリミットに達し、サイトは沈黙する。

「NotionはDBであり、Vercel Edge Networkがキャッシュ層である」という設計思想を持て。

推奨スタック

  • Backend: Notion Database (Status, Tags, Published Dateの管理)
  • Frontend: Next.js (App Router or Pages Router)
  • Deployment: Vercel
  • Communication: `@notionhq/client` + `notion-client` (高速なレンダリングのため)

—

2. APIレートリミット(3req/s)の死線を越えるキャッシュ戦略

Notion APIの最大の弱点は、1ページ取得するのにもプロパティ取得、ブロックリスト取得……と複数のリクエストを要する点だ。これを解決するには、ビルド時およびISR実行時にデータを「絞り取る」戦略が必要だ。

実装の要諦:並列リクエストの制御

大量のページをビルドする際、`Promise.all`で一気にリクエストを投げると、Notion APIは即座に 429 (Too Many Requests) を返す。これを防ぐために、リミッターを実装したフェッチ関数を定義せよ。

// lib/notion.ts
import { Client } from ‘@notionhq/client’;
import pLimit from ‘p-limit’; // 並列実行数を制限するライブラリ

const notion = new Client({ auth: process.env.NOTION_TOKEN });
const limit = pLimit(2); // 同時実行を2に制限し、余裕を持たせる

export const getPageData = async (pageId: string) => {
return limit(() => notion.pages.retrieve({ page_id: pageId }));
};

—

3. ISRによる「鮮度」と「速度」の両立

ISRは、ビルド後にバックグラウンドでページを再生成するNext.jsの至宝だ。これにより、Notion側で記事を更新した後、数秒〜数分で変更を反映させつつ、ユーザーには常にエッジキャッシュから静的ファイルを返すことができる。

実践:getStaticPropsでの実装(Pages Router例)

App Routerの場合は `revalidate = 60` のようなSegment Configを使用する。

// pages/posts/[slug].tsx
export const getStaticProps: GetStaticProps = async ({ params }) => {
const page = await getPageBySlug(params.slug); // 独自実装のページ取得関数

if (!page) return { notFound: true };

return {
props: {
post: page,
// 60秒ごとにバックグラウンドで再生成を試みる
// ユーザーのアクセスをトリガーに、Notion APIを1回だけ叩きに行く
revalidate: 60,
},
};
};

export const getStaticPaths: GetStaticPaths = async () => {
const posts = await getAllPublishedPosts();
return {
// 初回ビルド時に含めるページ。それ以外は初回アクセス時に生成(fallback: ‘blocking’)
paths: posts.map((p) => ({ params: { slug: p.slug } })),
fallback: ‘blocking’,
};
};

—

4. 開発効率を極限まで高める「隠れた知法」

ツールを使いこなす者は、マウスを触る時間を削る。

Notion 爆速ショートカット

  • `Cmd + Option + 1`:見出し1(構造化の基本)
  • `Cmd + L`:ページのURLをコピー(APIテスト時にIDを抜くのに必須)
  • `Cmd + J`:Notionのクイック検索(複数DB間の往来に)
  • `/code`:コードブロック即出し(言語指定は `typescript` を標準化せよ)

神プラグイン & ツール

  • Notion to Markdown (notion-to-md): Notionのブロック構造をクリーンなMarkdownに変換する。自前でパーサーを書く時間は、ビジネスロジックに充てるべきだ。
  • Save to Notion (Chrome Extension): 参考資料を特定のDBフォーマットで一瞬で保存。情報のサイロ化を防ぐ一歩。

—

5. チーム開発での設定共有化ルール(JSON構成案)

NotionのDBスキーマが変わると、Next.js側で型エラーが発生する。これを防ぐために、プロパティ名を共通定数として管理し、チームで共有せよ。

// constants/notion-schema.json
{
“DB_ID”: “xxxxxxxxxxxxxxxxxxxxxxxx”,
“PROPERTIES”: {
“TITLE”: “Name”,
“SLUG”: “Slug”,
“STATUS”: “Status”,
“PUBLISHED_AT”: “PublishedDate”,
“TAGS”: “Tags”
},
“STATUS_VALUES”: {
“PUBLISHED”: “Published”,
“DRAFT”: “Draft”,
“ARCHIVED”: “Archived”
}
}

このJSONを元に型定義を生成することで、「Notion側のカラム名を変えたらサイトが壊れた」という事故を未然に防ぐ。

—

結言:ナレッジの流動性を最大化せよ

Notion × Next.js ISRの真の価値は、「書く」という行為と「届ける」という行為の距離をゼロにすることにある。エンジニアがいちいちMarkdownをコミットし、PRをマージし、デプロイを待つ必要はない。

編集者がNotionで「公開」ステータスに切り替えた瞬間、VercelのISRが静かに、かつ確実に世界を書き換える。この「情報の高純度なパイプライン」を構築することこそが、モダンな開発チームにおけるテックリードの使命である。

君のチームのベロシティは、ドキュメントの置き場所一つで変わる。今すぐ、そのNotionをただのメモ帳から、最強のコンテンツ・エンジンへと昇華させよう。

タイトルとURLをコピーしました