インクリメンタル静的再生成 (ISR) の実装方法
インクリメンタル静的再生成 (ISR) を使用すると、以下のことが可能です:
- サイト全体を再ビルドせずに静的コンテンツを更新
- 事前レンダリングされた静的ページを提供することでサーバー負荷を軽減
- 適切な
cache-control
ヘッダーが自動的にページに追加されることを保証 - 大量のコンテンツページを
next build
の長時間化なしに処理
以下は最小限の例です:
interface Post {
id: string
title: string
content: string
}
// Next.js はリクエストが来たときにキャッシュを無効化します(最大60秒に1回)
export const revalidate = 60
// ビルド時に `generateStaticParams` からのパラメータのみ事前レンダリング
// 生成されていないパスへのリクエストがある場合、
// Next.js はオンデマンドでページをサーバーサイドレンダリングします
export const dynamicParams = true // または false にすると未知のパスで404
export async function generateStaticParams() {
const posts: Post[] = await fetch('https://api.vercel.app/blog').then((res) =>
res.json()
)
return posts.map((post) => ({
id: String(post.id),
}))
}
export default async function Page({
params,
}: {
params: Promise<{ id: string }>
}) {
const { id } = await params
const post: Post = await fetch(`https://api.vercel.app/blog/${id}`).then(
(res) => res.json()
)
return (
<main>
<h1>{post.title}</h1>
<p>{post.content}</p>
</main>
)
}
// Next.js はリクエストが来たときにキャッシュを無効化します(最大60秒に1回)
export const revalidate = 60
// ビルド時に `generateStaticParams` からのパラメータのみ事前レンダリング
// 生成されていないパスへのリクエストがある場合、
// Next.js はオンデマンドでページをサーバーサイドレンダリングします
export const dynamicParams = true // または false にすると未知のパスで404
export async function generateStaticParams() {
const posts = await fetch('https://api.vercel.app/blog').then((res) =>
res.json()
)
return posts.map((post) => ({
id: String(post.id),
}))
}
export default async function Page({ params }) {
const { id } = await params
const post = await fetch(`https://api.vercel.app/blog/${id}`).then((res) =>
res.json()
)
return (
<main>
<h1>{post.title}</h1>
<p>{post.content}</p>
</main>
)
}
この例の動作:
next build
時に既知のブログ投稿(この例では25件)が生成- これらのページ(例:
/blog/1
)へのリクエストはキャッシュされ即時表示 - 60秒経過後、次のリクエストではキャッシュされた(古い)ページが表示
- キャッシュが無効化され、バックグラウンドでページの新しいバージョン生成開始
- 生成成功後、Next.js は更新されたページを表示してキャッシュ
/blog/26
がリクエストされると、Next.js はオンデマンドでこのページを生成してキャッシュ
リファレンス
ルートセグメント設定
関数
例
時間ベースの再検証
これは /blog
でブログ投稿のリストを取得して表示します。1時間後、このページのキャッシュは次回訪問時に無効化されます。その後、バックグラウンドで最新のブログ投稿を含む新しいバージョンのページが生成されます。
interface Post {
id: string
title: string
content: string
}
export const revalidate = 3600 // 1時間ごとに無効化
export default async function Page() {
const data = await fetch('https://api.vercel.app/blog')
const posts: Post[] = await data.json()
return (
<main>
<h1>ブログ投稿</h1>
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</main>
)
}
export const revalidate = 3600 // 1時間ごとに無効化
export default async function Page() {
const data = await fetch('https://api.vercel.app/blog')
const posts = await data.json()
return (
<main>
<h1>ブログ投稿</h1>
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</main>
)
}
1秒ではなく1時間など、長めの再検証時間を設定することを推奨します。より精密な制御が必要な場合は、オンデマンド再検証を検討してください。リアルタイムデータが必要な場合は、動的レンダリングへの切り替えを検討してください。
revalidatePath
を使ったオンデマンド再検証
より精密な再検証方法として、revalidatePath
関数でオンデマンドにページを無効化できます。
例えば、新しい投稿を追加した後にこのサーバーアクションが呼び出されます。サーバーコンポーネントで fetch
を使用するかデータベースに接続するかにかかわらず、この操作はルート全体のキャッシュをクリアし、サーバーコンポーネントが新しいデータを取得できるようにします。
'use server'
import { revalidatePath } from 'next/cache'
export async function createPost() {
// キャッシュ内の /posts ルートを無効化
revalidatePath('/posts')
}
'use server'
import { revalidatePath } from 'next/cache'
export async function createPost() {
// キャッシュ内の /posts ルートを無効化
revalidatePath('/posts')
}
revalidateTag
を使ったオンデマンド再検証
ほとんどのユースケースでは、パス全体の再検証が推奨されます。より細かい制御が必要な場合は、revalidateTag
関数を使用できます。例えば、個々の fetch
呼び出しにタグを付けることができます:
export default async function Page() {
const data = await fetch('https://api.vercel.app/blog', {
next: { tags: ['posts'] },
})
const posts = await data.json()
// ...
}
export default async function Page() {
const data = await fetch('https://api.vercel.app/blog', {
next: { tags: ['posts'] },
})
const posts = await data.json()
// ...
}
ORM を使用している場合やデータベースに接続している場合は、unstable_cache
を使用できます:
import { unstable_cache } from 'next/cache'
import { db, posts } from '@/lib/db'
const getCachedPosts = unstable_cache(
async () => {
return await db.select().from(posts)
},
['posts'],
{ revalidate: 3600, tags: ['posts'] }
)
export default async function Page() {
const posts = getCachedPosts()
// ...
}
import { unstable_cache } from 'next/cache'
import { db, posts } from '@/lib/db'
const getCachedPosts = unstable_cache(
async () => {
return await db.select().from(posts)
},
['posts'],
{ revalidate: 3600, tags: ['posts'] }
)
export default async function Page() {
const posts = getCachedPosts()
// ...
}
その後、サーバーアクション または ルートハンドラー で revalidateTag
を使用できます:
'use server'
import { revalidateTag } from 'next/cache'
export async function createPost() {
// キャッシュ内の 'posts' タグが付いたすべてのデータを無効化
revalidateTag('posts')
}
'use server'
import { revalidateTag } from 'next/cache'
export async function createPost() {
// キャッシュ内の 'posts' タグが付いたすべてのデータを無効化
revalidateTag('posts')
}
未捕捉例外の処理
データの再検証中にエラーが発生した場合、最後に正常に生成されたデータがキャッシュから引き続き提供されます。次のリクエストで、Next.js はデータの再検証を再試行します。エラー処理について詳しく学ぶ。
キャッシュロケーションのカスタマイズ
キャッシュされたページとデータを永続ストレージに保持したり、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ランタイム使用時(デフォルト)のみサポートされます
- 静的エクスポート作成時にはISRはサポートされません
- 静的生成されたルート内で複数の
fetch
リクエストがあり、それぞれ異なるrevalidate
頻度が設定されている場合、ISRには最も短い時間が使用されます。ただし、各revalidate
頻度はデータキャッシュによって尊重されます - ルートで使用される
fetch
リクエストのいずれかがrevalidate
時間0
、または明示的なno-store
を持つ場合、そのルートは動的レンダリングされます - オンデマンドISRリクエストではミドルウェアは実行されません。つまり、パス書き換えやミドルウェア内のロジックは適用されません。正確なパスを再検証していることを確認してください。例: 書き換えられた
/post-1
ではなく/post/1
プラットフォームサポート
デプロイ方法 | サポート状況 |
---|---|
Node.jsサーバー | はい |
Dockerコンテナ | はい |
静的エクスポート | いいえ |
アダプター | プラットフォーム依存 |
Next.jsをセルフホスティングする際のISR設定方法について学べます。
バージョン履歴
バージョン | 変更内容 |
---|---|
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導入 |