コンテンツにスキップ
Pages Routerガイドドラフトモード

Next.jsでドラフトモードを使用してコンテンツをプレビューする方法

Pagesドキュメント」と「Data Fetchingドキュメント」では、getStaticPropsgetStaticPathsを使用して、ビルド時にページをプリレンダリングする方法(Static Generation)について説明しました。

Static Generationは、ヘッドレスCMSからデータを取得するページに役立ちます。しかし、ヘッドレスCMSでドラフトを作成し、そのドラフトをページですぐに表示したい場合には理想的ではありません。ビルド時ではなくリクエスト時にNext.jsにこれらのページをレンダリングさせ、公開コンテンツではなくドラフトコンテンツを取得させたいはずです。この特定のケースのみ、Next.jsにStatic Generationをバイパスさせたいでしょう。

Next.jsには、この問題を解決するDraft Modeという機能があります。以下に使用方法を説明します。

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

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

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

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

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

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

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

pages/api/draft.ts
// simple example for testing it manually from your browser.
export default function handler(req, res) {
  res.setDraftMode({ enable: true })
  res.end('Draft mode is enabled')
}

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

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

実際には、このAPIルートをヘッドレスCMSから*安全に*呼び出したいはずです。具体的な手順は、使用しているヘッドレスCMSによって異なりますが、以下に一般的な手順をいくつか示します。

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

まず、お好みのトークンジェネレーターを使用してシークレットトークン文字列を作成する必要があります。このシークレットは、Next.jsアプリとヘッドレスCMSのみが知っていることになります。これにより、CMSにアクセスできない人がドラフトURLにアクセスすることを防ぎます。

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

ターミナル
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) => {
  // Check the secret and next parameters
  // This secret should only be known to this API route and the CMS
  if (req.query.secret !== 'MY_SECRET_TOKEN' || !req.query.slug) {
    return res.status(401).json({ message: 'Invalid token' })
  }
 
  // Fetch the headless CMS to check if the provided `slug` exists
  // getPostBySlug would implement the required fetching logic to the headless CMS
  const post = await getPostBySlug(req.query.slug)
 
  // If the slug doesn't exist prevent draft mode from being enabled
  if (!post) {
    return res.status(401).json({ message: 'Invalid slug' })
  }
 
  // Enable Draft Mode by setting the cookie
  res.setDraftMode({ enable: true })
 
  // Redirect to the path from the fetched post
  // We don't redirect to req.query.slug as that might lead to open redirect vulnerabilities
  res.redirect(post.slug)
}

成功すると、ブラウザはドラフトモードCookie付きで目的のパスにリダイレクトされます。

ステップ2: getStaticPropsの更新

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

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

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

export async function getStaticProps(context) {
  if (context.draftMode) {
    // dynamic data
  }
}

ドラフト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として設定するか、手動でアクセスすると、ドラフトが表示されるはずです。

ターミナル
https://<your-site>/api/draft?secret=<token>&slug=<path>

詳細

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

ドラフトモードCookieを手動でクリアするには、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を使用してこのルートを呼び出す場合は、プリフェッチ中にCookieが誤って削除されるのを防ぐために、prefetch={false}を渡す必要があります。

getServerSidePropsと連携

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

知っておくと良いこと: ドラフトモードではCache-Controlヘッダーをバイパスできないため、設定しないでください。代わりに、ISRの使用をお勧めします。

APIルートと連携

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

export default function myApiRoute(req, res) {
  if (req.draftMode) {
    // get draft data
  }
}

next buildごとにユニーク

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

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

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