はじめに
Next.jsでの実装において、他システムの挙動をトリガーにして、手動でフェッチキャッシュの再検証を実施したい場面に遭遇しました。
調べると、revalidatePathとrevalidateTagという機能が使えそうでしたので紹介したいと思います。
前提
実装に当たっての前提条件は以下の通りです。
- Next.jsのバージョンは14以降でApp Routerを使用している
- 手動で再検証したいAPIはサーバーサイドコンポーネントのpage.tsxで使用しており、このコンポーネント内で他APIも呼んでいる
- このAPIの取得元データは他システムから更新される
- その際に、Router HandlerでNext.jsのキャッシュを再検証する処理を呼ぶ
revalidatePathとrevalidateTag
フェッチキャッシュを任意のタイミングで手動で再検証する方法としてrevalidatePathとrevalidateTagがあります。
Route Handlerを定義して別システムでデータ更新したタイミングで、Next.jsにアクセスしてキャッシュの再検証を行いたいと思います。
revalidatePath
- URLパス単位でキャッシュを再検証することができる
- 指定したURLパスのpage.tsxなどサーバーコンポーネントの中で呼ばれる全てのAPIのフェッチキャッシュが再検証される
- 逆に言うと、特定のフェッチキャッシュだけを再検証する事等は出来ない
- 実際の更新タイミングはrevalidatePathが呼ばれた後に、指定URLパスにアクセスする時
以下の例では、Route Handlersの中でrevalidatePathを実行して、
/test
ページで呼ばれる2つのフェッチキャッシュを再検証しています。
URLパスは動的なルートパラメーターを含んだ形の指定も可能のようです。(
/[slug]/test
など)
1 2 3 4 | export async function GET(request: Request) { revalidatePath("/test", "page"); return NextResponse.json({ revalidated: true, now: Date.now() }); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | export default async function Test() { const forex = await fetch("http://localhost/api/test/forex", { next: { revalidate: 1200 } }) .then((res) => { return res.json(); }) .then((res) => res.data); const forex2 = await fetch("http://localhost/api/test/forex2", { next: { revalidate: 1200 } }) .then((res) => { return res.json(); }) .then((res) => res.data); return ( <> <main className="mx-auto max-w-full lg:w-[1060px]"> test画面 <br /> forex1: {forex.usd_rate} <br /> forex2: {forex2.usd_rate} <br /> </main> </> ); } |
revalidateTag
- タグ単位でキャッシュを再検証することができる
- タグとはfetch時にオプションとして指定する値のこと
- ページを跨いで同じタグが指定された複数のフェッチキャッシュを再検証できる
- 実際の更新タイミングはrevalidateTagが呼ばれた後に、実際に各APIデータにアクセスする時
以下の例では、
testタグ
を指定して、このタグが指定されている2つのフェッチキャッシュを再検証しています。
1 2 3 4 | export async function GET(request: Request) { revalidateTag("test"); return NextResponse.json({ revalidated: true, now: Date.now() }); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | export default async function Test() { const forex = await fetch("http://localhost/api/test/forex", { next: { revalidate: 1200, tags: ["test"] } }) .then((res) => { return res.json(); }) .then((res) => res.data); const forex2 = await fetch("http://localhost/api/test/forex2", { next: { revalidate: 1200, tags: ["test"] } }) .then((res) => { return res.json(); }) .then((res) => res.data); return ( <> <main className="mx-auto max-w-full lg:w-[1060px]"> test画面 <br /> forex1: {forex.usd_rate} <br /> forex2: {forex2.usd_rate} <br /> </main> </> ); } |
今回の場合
再検証したいAPIと同じコンポーネント内で他APIも呼んでいるのでrevalidatePathを使用すると、不要なキャッシュも再検証されてしまいAPIアクセス回数が増えてしまいます。
今回の場合だとrevalidateTagを使用して、再検証したいAPIだけにタグ指定して、無駄なAPIアクセス回数を増やさない形で実装するのが適当かと思いました。