Menu

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

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('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にあることを前提としています。

Terminal
https:///api/draft?secret=&slug=
  • ``はデプロイメントドメインです。
  • ``は生成した秘密トークンに置き換えてください。
  • ``は表示したいページのパスです。/posts/fooを表示したい場合は、&slug=/posts/fooを使用します。

ヘッドレスCMSでは、ドラフトURLに変数を含めることができる場合があり、``をCMSのデータに基づいて動的に設定できます:&slug=/posts/{entry.fields.slug}

最後に、ドラフトAPIルートでは:

  • 秘密情報が一致し、slugパラメータが存在することを確認します(そうでない場合、リクエストは失敗します)。
  • res.setDraftModeを呼び出します。
  • その後、ブラウザをslugで指定されたパスにリダイレクトします(以下の例では307リダイレクトを使用しています)。
export default async (req, res) => {
  // secretとnextパラメータを確認
  // この秘密情報はこのAPIルートとCMSだけが知っているべきもの
  if (req.query.secret !== 'MY_SECRET_TOKEN' || !req.query.slug) {
    return res.status(401).json({ message: 'Invalid token' })
  }
 
  // 提供された`slug`が存在するかどうかを確認するためにヘッドレスCMSにフェッチ
  // getPostBySlugはヘッドレスCMSへの必要なフェッチロジックを実装する
  const post = await getPostBySlug(req.query.slug)
 
  // slugが存在しない場合、ドラフトモードの有効化を防止
  if (!post) {
    return res.status(401).json({ message: 'Invalid 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:///api/draft?secret=&slug=

詳細情報

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

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

ドラフトモードのクッキーを手動でクリアするには、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を使用してローカルでドラフトモードをテストするには、ブラウザがサードパーティクッキーとローカルストレージへのアクセスを許可する必要があります。