インクリメンタル静的再生成 (ISR)
インクリメンタル静的再生成(ISR)には以下のような利点があります:
- サイト全体を再ビルドすることなく静的コンテンツを更新できます
- ほとんどのリクエストに対して事前レンダリングされた静的ページを提供することでサーバーの負荷を軽減します
- ページに適切な
cache-control
ヘッダーが自動的に追加されます next build
の時間を長くすることなく、大量のコンテンツページを処理できます
以下は最小限の例です:
import type { GetStaticPaths, GetStaticProps } from 'next'
interface Post {
id: string
title: string
content: string
}
interface Props {
post: Post
}
export const getStaticPaths: GetStaticPaths = async () => {
const posts = await fetch('https://api.vercel.app/blog').then((res) =>
res.json()
)
const paths = posts.map((post: Post) => ({
params: { id: String(post.id) },
}))
// ビルド時にはこれらのパスのみを事前レンダリングします。
// { fallback: 'blocking' }は、パスが存在しない場合に
// オンデマンドでページをサーバーレンダリングします。
return { paths, fallback: false }
}
export const getStaticProps: GetStaticProps<Props> = async ({
params,
}: {
params: { id: string }
}) => {
const post = await fetch(`https://api.vercel.app/blog/${params.id}`).then(
(res) => res.json()
)
return {
props: { post },
// Next.jsは60秒に1回まで、リクエストが来た際に
// キャッシュを無効化します。
revalidate: 60,
}
}
export default function Page({ post }: Props) {
return (
<main>
<h1>{post.title}</h1>
<p>{post.content}</p>
</main>
)
}
この例の動作は以下の通りです:
next build
の間に、既知のすべてのブログ記事が生成されます(この例では25記事)- これらのページへのすべてのリクエスト(例:
/blog/1
)はキャッシュされ、即座に返されます - 60秒経過後も、次のリクエストはキャッシュされた(古い)ページを表示します
- キャッシュが無効化され、新しいバージョンのページがバックグラウンドで生成を開始します
- 正常に生成されると、Next.jsは更新されたページを表示してキャッシュします
/blog/26
がリクエストされた場合、Next.jsはこのページをオンデマンドで生成してキャッシュします
リファレンス
関数
例
res.revalidate()
を使用したオンデマンド検証
より正確な再検証方法として、API Routerからres.revalidate
を使用して、オンデマンドで新しいページを生成できます。
例えば、このAPI Routeは/api/revalidate?secret=<token>
で呼び出され、指定されたブログ記事を再検証します。Next.jsアプリケーションのみが知る秘密トークンを作成してください。この秘密は再検証APIルートへの不正アクセスを防ぐために使用されます。
import type { NextApiRequest, NextApiResponse } from 'next'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
// 有効なリクエストであることを確認するために秘密をチェック
if (req.query.secret !== process.env.MY_SECRET_TOKEN) {
return res.status(401).json({ message: 'Invalid token' })
}
try {
// これは実際のパスである必要があり、書き換えられたパスではありません
// 例:"/posts/[id]"の場合、これは"/posts/1"となります
await res.revalidate('/posts/1')
return res.json({ revalidated: true })
} catch (err) {
// エラーが発生した場合、Next.jsは
// 最後に正常に生成されたページを表示し続けます
return res.status(500).send('Error revalidating')
}
}
オンデマンド再検証を使用する場合、getStaticProps
内でrevalidate
時間を指定する必要はありません。Next.jsはデフォルト値のfalse
(再検証なし)を使用し、res.revalidate()
が呼び出されたときにのみページをオンデマンドで再検証します。
キャッチされない例外の処理
バックグラウンド再生成を処理する際にgetStaticProps
内でエラーが発生した場合、または手動でエラーをスローした場合、最後に正常に生成されたページが引き続き表示されます。次の後続のリクエストで、Next.jsはgetStaticProps
の呼び出しを再試行します。
import type { GetStaticProps } from 'next'
interface Post {
id: string
title: string
content: string
}
interface Props {
post: Post
}
export const getStaticProps: GetStaticProps<Props> = async ({
params,
}: {
params: { id: string }
}) => {
// このリクエストでキャッチされないエラーがスローされた場合、
// Next.jsは現在表示されているページを無効化せず、
// 次のリクエストで getStaticProps を再試行します。
const res = await fetch(`https://api.vercel.app/blog/${params.id}`)
const post: Post = await res.json()
if (!res.ok) {
// サーバーエラーがある場合、
// 次の成功したリクエストまでキャッシュが更新されないように、
// 返却する代わりにエラーをスローすることをお勧めします。
throw new Error(`Failed to fetch posts, received status ${res.status}`)
}
return {
props: { post },
// Next.jsは60秒に1回まで、リクエストが来た際に
// キャッシュを無効化します。
revalidate: 60,
}
}
キャッシュの場所のカスタマイズ
ページのキャッシュと再検証(インクリメンタル静的再生成を使用)は、同じ共有キャッシュを使用します。Vercelにデプロイする場合、ISRキャッシュは自動的に永続ストレージに保存されます。
セルフホスティングの場合、ISRキャッシュはNext.jsサーバーのファイルシステム(ディスク)に保存されます。これはPagesとApp Routerの両方を使用してセルフホスティングする際に自動的に機能します。
キャッシュされたページとデータを永続ストレージに保存したい場合や、Next.jsアプリケーションの複数のコンテナまたはインスタンス間でキャッシュを共有したい場合は、Next.jsのキャッシュの場所を設定できます。詳細はこちらをご覧ください。
トラブルシューティング
ローカル開発でのキャッシュされたデータのデバッグ
fetch
APIを使用している場合、どのリクエストがキャッシュされているか、またはキャッシュされていないかを理解するために追加のログを追加できます。logging
オプションの詳細はこちらをご覧ください。
module.exports = {
logging: {
fetches: {
fullUrl: true,
},
},
}
本番動作の確認
本番環境でページが正しくキャッシュされ、再検証されていることを確認するには、next build
を実行し、その後next start
を実行して本番のNext.jsサーバーを実行することでローカルでテストできます。
これにより、本番環境と同様のISRの動作をテストすることができます。さらにデバッグを行うには、.env
ファイルに以下の環境変数を追加してください:
NEXT_PRIVATE_DEBUG_CACHE=1
これにより、Next.jsサーバーはISRキャッシュのヒットとミスをコンソールログに出力します。この出力を確認することで、next build
中に生成されたページや、パスがオンデマンドでアクセスされた際にページがどのように更新されるかを確認できます。
注意事項
- ISRはNode.jsランタイム(デフォルト)を使用する場合のみサポートされています。
- Static Exportを作成する場合、ISRはサポートされていません。
- オンデマンドISRリクエストに対してミドルウェアは実行されません。つまり、ミドルウェアのパスの書き換えやロジックは適用されません。正確なパスを再検証していることを確認してください。例えば、書き換えられた
/post-1
ではなく、/post/1
を使用してください。
バージョン履歴
バージョン | 変更内容 |
---|---|
v14.1.0 | カスタムcacheHandler が安定版に |
v13.0.0 | App Routerが導入 |
v12.2.0 | Pages Router: オンデマンドISRが安定版に |
v12.0.0 | Pages Router: ボットを意識したISRフォールバックが追加 |
v9.5.0 | Pages Router: 安定版ISRが導入 |