コンテンツにスキップ

ドラフトモード

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

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

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
// 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 という名前のクッキーを含む Set-Cookie レスポンスヘッダーが表示されます。

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

実際には、ヘッドレスCMSからこの API ルートを安全に呼び出すことをお勧めします。具体的な手順は使用しているヘッドレス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に変数を含めることができ、CMSのデータに基づいて <path> を動的に設定できる場合があります。例: &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)
}

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

ステップ2: getStaticProps を更新する

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

クッキーが設定された状態で(res.setDraftMode を介して)getStaticProps を持つページをリクエストすると、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から、または手動でドラフトAPIルート(secretslug を含む)にアクセスすると、ドラフトコンテンツが表示されるはずです。そして、公開せずにドラフトを更新した場合でも、ドラフトを表示できるはずです。

これをヘッドレスCMSのドラフトURLとして設定するか、手動でアクセスすると、ドラフトが表示されるはずです。

ターミナル
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) {
    // get draft data
  }
}

next build ごとにユニーク

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

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

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