はじめに/ガイド/Draft Mode

Next.jsでDraft Modeを使用してコンテンツをプレビューする方法

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

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

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

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

まず、APIルートを作成します。任意の名前(例: pages/api/draft.ts)を付けることができます。

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

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

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

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

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

ブラウザの開発者ツールを開いて/api/draftにアクセスすると、__prerender_bypassという名前のCookieを持つ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が存在しない場合、Draft Modeを有効にしない
  if (!post) {
    return res.status(401).json({ message: '無効なslugです' })
  }

  // Cookieを設定してDraft Modeを有効化
  res.setDraftMode({ enable: true })

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

成功すると、ブラウザはDraft Mode Cookieと共にプレビューしたいパスにリダイレクトされます。

ステップ2: getStaticPropsの更新

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

Cookieが設定された状態(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から(secretslugを使用して)下書きAPIルートにアクセスするか、手動でアクセスすると、下書きコンテンツを表示できるようになります。また、公開せずに下書きを更新した場合も、下書きを表示できるはずです。

ヘッドレスCMSで下書きURLとして設定するか、手動でアクセスすると下書きを表示できます:

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

詳細情報

Draft Mode Cookieのクリア

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

Draft Mode Cookieを手動でクリアするには、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を使用してこのルートを呼び出す場合、プリフェッチ時に誤ってCookieが削除されないようにprefetch={false}を渡す必要があります。

getServerSidePropsとの連携

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

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

APIルートとの連携

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

export default function myApiRoute(req, res) {
  if (req.draftMode) {
    // 下書きデータを取得
  }
}

next buildごとに一意

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

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

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