【Next.js和訳】Advanced Features/Preview Mode
この記事について
この記事は、Advanced Features/Preview Modeの記事を和訳したものです。
記事内で使用する画像は、公式ドキュメント内の画像を引用して使用させていただいております。
Peview Mode
例
- WordPress Example (Demo)
- DatoCMS Example (Demo)
- TakeShape Example (Demo)
- Sanity Example (Demo)
- Prismic Example (Demo)
- Contentful Example (Demo)
- Strapi Example (Demo)
- Prepr Example (Demo)
- Agility CMS Example (Demo)
- Cosmic Example (Demo)
- ButterCMS Example (Demo)
- Storyblok Example (Demo)
- GraphCMS Example (Demo)
- Kontent Example (Demo)
Pages ドキュメントとData Fetching ドキュメントでは、getStaticProps
とgetStaticPaths
を使用して、構築時にページを事前にレンダリングする方法(Static Generation)について説明しました。
静的生成は、ページがヘッドレス CMS からデータを取得する場合に便利です。しかし、ヘッドレス CMS 上で Draft を書いていて、その Draft をすぐにページ上でプレビューしたい場合には理想的ではありません。Next.js はこれらのページをビルド時ではなくリクエスト時にレンダリングし、公開されたコンテンツではなくドラフトのコンテンツを取得したいと思います。Next.js が Static Generation をバイパスするのは、このような特定のケースに限られます。
Next.js には、この問題を解決するプレビューモードという機能があります。以下に、その使い方を説明します。
Step 1.preview API route の作成とアクセス
まず、プレビュー用の API ルートを作成します。名前は自由で、例えばpages/api/preview.js
(TypeScript を使っている場合は.ts
)のようにします。
この API ルートでは、レスポンスオブジェクトに対して、setPreviewData
を呼び出す必要があります。setPreviewData
の引数にはオブジェクトを指定する必要があり、これはgetStaticProps
で使用できます(詳細は後述します)。今のところ、{}
を使うことにします。
export default function handler(req, res) {
// ,,,
res.setPreviewData({})
// ,,,
}
res.setPreviewData
では、プレビューモードをオンにするブラウザにいくつかのcookiesを設定します。これらのクッキーを含む Next.js へのリクエストは、プレビューモードとみなされ、静的に生成されたページの動作が変更されます(詳細は後述します)。
以下のような API ルートを作成し、ブラウザから手動でアクセスすることで、手動でテストすることができます。
// ブラウザから手動でテストするための簡単な例です。
// これがpages/api/preview.jsにある場合は
// ブラウザから/api/previewを開いてください。
export default function handler(req, res) {
res.setPreviewData({})
res.end("Preview mode enabled")
}
ブラウザの開発者ツールを使うと、このリクエストで__prerender_bypass
と__next_preview_data
の cookie が設定されることに気づくでしょう。
ヘッドレス CMS から安全にアクセス
実際には、ヘッドレス CMS からこの API ルートを安全に呼び出したいと思います。具体的な手順は、使用しているヘッドレス CMS によって異なりますが、ここでは一般的な手順をご紹介します。
これらの手順は、使用しているヘッドレス CMS がカスタムプレビュー URLの設定をサポートしていることを前提としています。対応していない場合でも、この方法でプレビュー URL を確保することはできますが、プレビュー URL を構築して手動でアクセスする必要があります。
First, お好みのトークンジェネレータを使って秘密のトークン文字列を作成してください。この秘密は、Next.js アプリとヘッドレス CMS だけが知っているものです。この秘密は、あなたの CMS にアクセスしていない人がプレビュー URL にアクセスすることを防ぎます。
Second, お使いのヘッドレス CMS がカスタムプレビュー URL の設定をサポートしている場合は、プレビュー URL として以下を指定します。(これは、プレビュー API ルートが pages/api/preview.js
にあることを前提としています。)
https://<your-site>/api/preview?secret=<token>&slug=<path>
-
<your-site>
にはデプロイメントドメインを指定してください。 -
<token>
は、生成したシークレットトークンに置き換えてください。 -
<path>
には、プレビューしたいページのパスを指定します。もし/posts/foo
をプレビューしたいのであれば、&slug=/posts/foo
としてください。
ヘッドレス CMS では、プレビュー URL に変数を含めることができるので、<path>
を CMS のデータに基づいて以下のように動的に設定することができるかもしれません。&slug=/posts/{entry.fields.slug}
。
Finally, プレビュー API ルート:
- シークレットが一致していることと、
slug
パラメータが存在することを確認します(存在しない場合、リクエストは失敗します)。 -
res.setPreviewData
を呼び出します。 - その後、ブラウザを
slug
で指定したパスにリダイレクトします。(以下の例では307 redirectを使用しています)。
export default async (req, res) => {
// シークレットと次のパラメータを確認する
// このシークレットは、この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.setPreviewData({})
// 取得した記事のパスにリダイレクトする
// req.query.slugへのリダイレクトは、オープンリダイレクトの脆弱性につながる可能性があるので行いません。
res.redirect(post.slug)
}
成功すれば、プレビューモードのクッキーが設定された状態で、ブラウザがプレビューしたいパスにリダイレクトされます。
getStaticProps
の更新
Step 2. 次のステップでは、プレビューモードをサポートするために getStaticProps
を更新します。
プレビューモードのクッキーが設定された getStaticProps
を持つページを (res.setPreviewData
経由で) リクエストすると、getStaticProps
は (ビルド時ではなく) リクエスト時 に呼び出されます。
さらに、これは context
オブジェクトを使って呼び出され、以下のようになります。
-
context.preview
はtrue
になります。 -
context.previewData
はsetPreviewData
で使用される引数と同じになります。
export async function getStaticProps(context) {
// プレビューモードのクッキーが設定された状態でこのページをリクエストした場合。
//
// - context.preview は true になります。
// - context.previewData は、以下と同じになります。
// `setPreviewData' で使用される引数です。
}
プレビュー API のルートでは、res.setPreviewData({})
を使用しているので、context.previewData
は{}
となります。これを利用して、必要に応じてプレビュー API ルートから getStaticProps
にセッション情報を渡すことができます。
また、getStaticPaths
も使用している場合は、context.params
も使用可能になります。
プレビューデータの取得
getStaticProps
を更新して、context.preview
や context.previewData
に基づいて異なるデータをフェッチすることができます。
例えば、ヘッドレス CMS がドラフト投稿に対して異なる API エンドポイントを持っているかもしれません。その場合は、context.preview
を使って、API エンドポイントの URL を以下のように変更することができます。
export async function getStaticProps(context) {
// context.previewがtrueの場合、APIエンドポイントに"/preview "を追加します。
// 公開されたデータではなく、ドラフトデータを要求するためです。これは、使用しているヘッドレスCMSによって
// 使用しているヘッドレスCMSによって異なります。
const res = await fetch(`https://.../${context.preview ? "preview" : ""}`)
// ...
}
これで完了です。ヘッドレス CMS からプレビュー API ルート(secret
とslug
を含む)にアクセスするか、手動でアクセスすると、プレビューコンテンツを見ることができるようになるはずです。また、公開せずにドラフトを更新すると、ドラフトをプレビューできるようになるはずです
# ヘッドレスCMSのプレビューURLとして設定するか、手動でアクセスしてください。
# そうすれば、プレビューを見ることができます。
https://<your-site>/api/preview?secret=<token>&slug=<path>
詳細
プレビューモードのクッキーを消去
デフォルトでは、プレビューモードの Cookie に有効期限が設定されていないため、ブラウザを閉じた時点でプレビューモードが終了してしまいます。
プレビュークッキーを手動で消去するには、clearPreviewData
を呼び出す API ルートを作成して、この API ルートにアクセスします。
export default function handler(req, res) {
// プレビューモードのクッキーをクリアします。
// この関数は引数をとりません。
res.clearPreviewData()
// ,,,
}
プレビューモードの継続時間の指定
setPreviewData
はオプションで 2 番目のパラメータを取ります。これはオプションオブジェクトでなければなりません。次のようなキーを受け取ります。
-
maxAge
: プレビューセッションの継続時間を秒単位で指定します。
setPreviewData(data, {
maxAge: 60 * 60, // プレビューモードのクッキーは、1時間で期限切れになります。
})
previewData
のサイズ制限
setPreviewData
にオブジェクトを渡して、それをgetStaticProps
で利用できるようにすることができます。ただし、データはクッキーに保存されるため、サイズに制限があります。現在、プレビューデータのサイズは 2KB までです。
getServerSideProps
で動作
プレビューモードは、getServerSideProps
でも動作します。また、preview
と previewData
を含む context
オブジェクトでも利用可能になります。
API ルートとの連携
API ルートは、リクエストオブジェクトの下にある preview
と previewData
にアクセスできます。例えば、以下のようになります
export default function myApiRoute(req, res) {
const isPreview = req.preview
const previewData = req.previewData
// ...
}
next build
Unique per バイパスクッキーの値と、previewData
を暗号化するための秘密鍵は、next build
が完了すると変更されます。これにより、バイパスクッキーが推測されないことが保証されます。
もっと詳しく
以下のページも参考になります。
Discussion