ドラフトモード

Pagesドキュメントデータ取得ドキュメントでは、getStaticPropsgetStaticPathsを使用してビルド時にページを事前レンダリングする方法(静的生成)について説明しました。

静的生成はヘッドレスCMSからデータを取得するページに有効ですが、ヘッドレスCMSでドラフトを執筆中にすぐにプレビューしたい場合には理想的ではありません。このような場合、Next.jsにビルド時ではなくリクエスト時にページをレンダリングさせ、公開済みコンテンツではなくドラフトコンテンツを取得させる必要があります。Next.jsにこの特定のケースでのみ静的生成をバイパスさせたいでしょう。

Next.jsにはこの問題を解決するドラフトモード機能があります。以下に使用方法を説明します。

ステップ1: APIルートの作成とアクセス

Next.jsのAPIルートに慣れていない場合は、まずAPIルートドキュメントをご覧ください。

まず、APIルートを作成します。任意の名前(例: pages/api/draft.ts)で作成可能です。

このAPIルートでは、レスポンスオブジェクトに対してsetDraftModeを呼び出す必要があります。

export default function handler(req, res) {
  // ...
  res.setDraftMode({ enable: true })
  // ...
}

これによりドラフトモードを有効にするクッキーが設定されます。このクッキーを含む後続のリクエストはドラフトモードをトリガーし、静的に生成されたページの動作を変更します(詳細は後述)。

以下のようなAPIルートを作成し、ブラウザから手動でアクセスすることでテストできます:

pages/api/draft.ts
// ブラウザから手動でテストするための簡単な例
export default function handler(req, res) {
  res.setDraftMode({ enable: true })
  res.end('ドラフトモードが有効になりました')
}

ブラウザの開発者ツールを開いて/api/draftにアクセスすると、__prerender_bypassという名前のクッキーを持つSet-Cookieレスポンスヘッダーが確認できます。

ヘッドレスCMSから安全にアクセス

実際には、ヘッドレスCMSからこのAPIルートに安全にアクセスする必要があります。使用するヘッドレスCMSによって具体的な手順は異なりますが、一般的な手順を以下に示します。

これらの手順は、使用するヘッドレスCMSがカスタムドラフトURLの設定をサポートしていることを前提としています。サポートしていない場合でも、この方法を使用してドラフトURLを保護できますが、ドラフトURLを手動で構築してアクセスする必要があります。

まず、任意のトークンジェネレーターを使用して秘密トークン文字列を作成します。この秘密はNext.jsアプリとヘッドレスCMSのみが知っている状態にします。これにより、CMSにアクセス権限のない人がドラフトURLにアクセスするのを防ぎます。

次に、ヘッドレスCMSがカスタムドラフトURLの設定をサポートしている場合、以下のURLをドラフトURLとして指定します。ドラフトAPIルートがpages/api/draft.tsにあると仮定します。

Terminal
https://<your-site>/api/draft?secret=<token>&slug=<path>
  • <your-site>はデプロイドメインに置き換えます
  • <token>は生成した秘密トークンに置き換えます
  • <path>は表示したいページのパスです。/posts/fooを表示したい場合は&slug=/posts/fooとします

ヘッドレスCMSによっては、ドラフトURLに変数を含めることができ、<path>をCMSのデータに基づいて動的に設定できます(例: &slug=/posts/{entry.fields.slug})。

最後に、ドラフトAPIルートで以下の処理を行います:

  • 秘密トークンが一致し、slugパラメータが存在するか確認します(どちらかが欠けている場合、リクエストは失敗します)
  • res.setDraftModeを呼び出します
  • ブラウザをslugで指定されたパスにリダイレクトします(以下の例では307リダイレクトを使用)
export default async (req, res) => {
  // 秘密トークンと次のパラメータを確認
  // この秘密はこのAPIルートとCMSのみが知っている必要がある
  if (req.query.secret !== 'MY_SECRET_TOKEN' || !req.query.slug) {
    return res.status(401).json({ message: '無効なトークンです' })
  }

  // 提供された`slug`が存在するかヘッドレスCMSに確認
  // getPostBySlugはヘッドレスCMSへの取得ロジックを実装
  const post = await getPostBySlug(req.query.slug)

  // slugが存在しない場合、ドラフトモードを有効にしない
  if (!post) {
    return res.status(401).json({ message: '無効なslugです' })
  }

  // クッキーを設定してドラフトモードを有効化
  res.setDraftMode({ enable: true })

  // 取得した投稿のパスにリダイレクト
  // req.query.slugにリダイレクトしない(オープンリダイレクト脆弱性の可能性があるため)
  res.redirect(post.slug)
}

成功すると、ブラウザはドラフトモードのクッキーと共に表示したいパスにリダイレクトされます。

ステップ2: getStaticPropsの更新

次のステップは、ドラフトモードをサポートするようにgetStaticPropsを更新することです。

クッキーが設定された状態(res.setDraftMode経由)でgetStaticPropsを持つページをリクエストすると、getStaticPropsはビルド時ではなくリクエスト時に呼び出されます。

さらに、context.draftModetrueになるcontextオブジェクトと共に呼び出されます。

export async function getStaticProps(context) {
  if (context.draftMode) {
    // 動的データ
  }
}

ドラフトAPIルートでres.setDraftModeを使用したため、context.draftModetrueになります。

getStaticPathsも使用している場合、context.paramsも利用可能です。

ドラフトデータの取得

context.draftModeに基づいて異なるデータを取得するようにgetStaticPropsを更新できます。

例えば、ヘッドレスCMSがドラフト投稿用の異なるAPIエンドポイントを持っている場合、以下のようにAPIエンドポイントURLを変更できます:

export async function getStaticProps(context) {
  const url = context.draftMode
    ? 'https://draft.example.com'
    : 'https://production.example.com'
  const res = await fetch(url)
  // ...
}

これで完了です!ヘッドレスCMSからまたは手動でドラフトAPIルート(secretslug付き)にアクセスすると、ドラフトコンテンツを表示できるようになります。また、公開せずにドラフトを更新した場合も、ドラフトを表示できるはずです。

ヘッドレスCMSでドラフトURLとして設定するか、手動でアクセスするとドラフトを表示できます。

Terminal
https://<your-site>/api/draft?secret=<token>&slug=<path>

詳細情報

ドラフトモードクッキーのクリア

デフォルトでは、ドラフトモードセッションはブラウザを閉じると終了します。

ドラフトモードクッキーを手動でクリアするには、setDraftMode({ enable: false })を呼び出すAPIルートを作成します:

pages/api/disable-draft.ts
export default function handler(req, res) {
  res.setDraftMode({ enable: false })
}

その後、/api/disable-draftにリクエストを送信してAPIルートを呼び出します。next/linkを使用してこのルートを呼び出す場合、プリフェッチ時に誤ってクッキーを削除しないようにprefetch={false}を渡す必要があります。

getServerSidePropsとの連携

ドラフトモードはgetServerSidePropsと連携し、contextオブジェクトのdraftModeキーとして利用可能です。

知っておくと良い: ドラフトモードを使用する場合、Cache-Controlヘッダーを設定すべきではありません。なぜならバイパスできないためです。代わりにISRの使用を推奨します。

APIルートとの連携

APIルートはリクエストオブジェクトのdraftModeにアクセスできます。例えば:

export default function myApiRoute(req, res) {
  if (req.draftMode) {
    // ドラフトデータを取得
  }
}

next buildごとに一意

next buildを実行するたびに、新しいバイパスクッキー値が生成されます。

これにより、バイパスクッキーが推測されるのを防ぎます。

知っておくと良い: HTTP経由でローカルでドラフトモードをテストするには、ブラウザでサードパーティクッキーとローカルストレージへのアクセスを許可する必要があります。