インクリメンタル静的再生成 (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) },
}))
// We'll prerender only these paths at build time.
// { fallback: 'blocking' } will server-render pages
// on-demand if the path doesn't exist.
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 will invalidate the cache when a
// request comes in, at most once every 60 seconds.
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ルーターからオンデマンドで新しいページを生成するためにres.revalidate
を使用します。
例えば、このAPIルートは/api/revalidate?secret=<token>
で呼び出すことができ、指定されたブログ記事を再検証します。Next.jsアプリのみが知るシークレットトークンを作成してください。このシークレットは、再検証APIルートへの不正アクセスを防ぐために使用されます。
import type { NextApiRequest, NextApiResponse } from 'next'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
// Check for secret to confirm this is a valid request
if (req.query.secret !== process.env.MY_SECRET_TOKEN) {
return res.status(401).json({ message: 'Invalid token' })
}
try {
// This should be the actual path not a rewritten path
// e.g. for "/posts/[id]" this should be "/posts/1"
await res.revalidate('/posts/1')
return res.json({ revalidated: true })
} catch (err) {
// If there was an error, Next.js will continue
// to show the last successfully generated page
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 }
}) => {
// If this request throws an uncaught error, Next.js will
// not invalidate the currently shown page and
// retry getStaticProps on the next request.
const res = await fetch(`https://api.vercel.app/blog/${params.id}`)
const post: Post = await res.json()
if (!res.ok) {
// If there is a server error, you might want to
// throw an error instead of returning so that the cache is not updated
// until the next successful request.
throw new Error(`Failed to fetch posts, received status ${res.status}`)
}
return {
props: { post },
// Next.js will invalidate the cache when a
// request comes in, at most once every 60 seconds.
revalidate: 60,
}
}
キャッシュの場所をカスタマイズする
ページをキャッシュし、再検証する (インクリメンタル静的再生成を使用) 際は、同じ共有キャッシュを使用します。Vercelにデプロイするすると、ISRキャッシュは自動的に永続ストレージに永続化されます。
自己ホスティングの場合、ISRキャッシュはNext.jsサーバーのファイルシステム(ディスク上)に保存されます。これは、Pages Routerと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ランタイム (デフォルト) を使用している場合にのみサポートされます。
- 静的エクスポートを作成する場合、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が導入されました。 |
この情報は役に立ちましたか?