ドラフトモード

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

静的生成はヘッドレスCMSからデータを取得するページに有効です。しかし、ヘッドレスCMSでドラフトを執筆中に、すぐにページ上でドラフトを確認したい場合には理想的ではありません。このような場合、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を指定します。ドラフト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: '無効なトークンです' })
  }

  // ヘッドレスCMSに問い合わせて、提供された`slug`が存在するか確認
  // 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を更新してドラフトモードをサポートすることです。

getStaticPropsを持つページにクッキーが設定された状態(res.setDraftMode経由)でリクエストすると、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ルートを呼び出すために/api/disable-draftにリクエストを送信します。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経由でローカルでドラフトモードをテストするには、ブラウザでサードパーティクッキーとローカルストレージへのアクセスを許可する必要があります。