Menu

キャッシングと再検証

キャッシングは、データ取得やその他の計算結果を保存し、同じデータへの今後のリクエストを再度計算することなく高速に処理できるようにする技術です。再検証により、アプリケーション全体を再構築することなく、キャッシュエントリを更新できます。

Next.jsはキャッシングと再検証を処理するためのAPIをいくつか提供しています。このガイドでは、それらをいつどのように使用するかについて説明します。

fetch

デフォルトでは、fetchリクエストはキャッシュされません。cacheオプションを'force-cache'に設定して、個別のリクエストをキャッシュできます。

app/page.tsx
TypeScript
export default async function Page() {
  const data = await fetch('https://...', { cache: 'force-cache' })
}

補足fetchリクエストはデフォルトではキャッシュされませんが、Next.jsはfetchリクエストを含むルートをプリレンダリングし、HTMLをキャッシュします。ルートが動的であることを保証したい場合は、connection APIを使用してください。

fetchリクエストによって返されたデータを再検証するには、next.revalidateオプションを使用できます。

app/page.tsx
TypeScript
export default async function Page() {
  const data = await fetch('https://...', { next: { revalidate: 3600 } })
}

これにより、指定された秒数の後にデータが再検証されます。

詳細は、fetch APIリファレンスをご覧ください。

unstable_cache

unstable_cacheを使用すると、データベースクエリやその他の非同期関数の結果をキャッシュできます。これを使用するには、関数をunstable_cacheでラップしてください。例:

app/lib/data.ts
TypeScript
import { db } from '@/lib/db'
export async function getUserById(id: string) {
  return db
    .select()
    .from(users)
    .where(eq(users.id, id))
    .then((res) => res[0])
}
app/page.tsx
TypeScript
import { unstable_cache } from 'next/cache'
import { getUserById } from '@/app/lib/data'
 
export default async function Page({
  params,
}: {
  params: Promise<{ userId: string }>
}) {
  const { userId } = await params
 
  const getCachedUser = unstable_cache(
    async () => {
      return getUserById(userId)
    },
    [userId] // キャッシュキーにユーザーIDを追加
  )
}

この関数は、キャッシュをどのように再検証するかを定義する、3番目のオプションのオブジェクトを受け入れます。以下のオプションを受け入れます:

  • tags:Next.jsがキャッシュを再検証するために使用するタグの配列。
  • revalidate:キャッシュを再検証するまでの秒数。
app/page.tsx
TypeScript
const getCachedUser = unstable_cache(
  async () => {
    return getUserById(userId)
  },
  [userId],
  {
    tags: ['user'],
    revalidate: 3600,
  }
)

詳細は、unstable_cache APIリファレンスをご覧ください。

revalidateTag

revalidateTagは、タグに基づくキャッシュエントリを再検証し、イベント後に実行するために使用されます。この関数は現在、2つの動作をサポートしています:

  • profile="max"付き:stale-while-revalidateセマンティクスを使用し、バックグラウンドで新しいコンテンツを取得中に古いコンテンツを提供します
  • 2番目の引数なし:キャッシュを直ちに期限切れにする従来の動作(非推奨)

fetchで使用するには、next.tagsオプションで関数にタグを付けることから始めてください:

app/lib/data.ts
TypeScript
export async function getUserById(id: string) {
  const data = await fetch(`https://...`, {
    next: {
      tags: ['user'],
    },
  })
}

または、tagsオプションでunstable_cache関数にマークを付けることができます:

app/lib/data.ts
TypeScript
export const getUserById = unstable_cache(
  async (id: string) => {
    return db.query.users.findFirst({ where: eq(users.id, id) })
  },
  ['user'], // 変数がパラメータとして渡されていない場合は必須
  {
    tags: ['user'],
  }
)

次に、ルートハンドラーまたはServer ActionでrevalidateTagを呼び出します:

app/lib/actions.ts
TypeScript
import { revalidateTag } from 'next/cache'
 
export async function updateUser(id: string) {
  // データを変更
  revalidateTag('user', 'max') // 推奨:stale-while-revalidateを使用
}

複数の関数で同じタグを再利用し、すべてを一度に再検証できます。

詳細は、revalidateTag APIリファレンスをご覧ください。

revalidatePath

revalidatePathは、ルートを再検証し、イベント後に実行するために使用されます。これを使用するには、ルートハンドラーまたはServer Actionで呼び出してください:

app/lib/actions.ts
TypeScript
import { revalidatePath } from 'next/cache'
 
export async function updateUser(id: string) {
  // データを変更
  revalidatePath('/profile')

詳細は、revalidatePath APIリファレンスをご覧ください。

updateTag

updateTagは、Server Actionで読み取り後の自分の書き込みシナリオ用にキャッシュデータを直ちに期限切れにするように特に設計されています。revalidateTagとは異なり、Server Action内でのみ使用でき、キャッシュエントリを直ちに期限切れにします。

app/lib/actions.ts
TypeScript
import { updateTag } from 'next/cache'
import { redirect } from 'next/navigation'
 
export async function createPost(formData: FormData) {
  // データベースにポストを作成
  const post = await db.post.create({
    data: {
      title: formData.get('title'),
      content: formData.get('content'),
    },
  })
 
  // キャッシュを直ちに期限切れにして、新しいポストが表示されるようにする
  updateTag('posts')
  updateTag(`post-${post.id}`)
 
  redirect(`/posts/${post.id}`)
}

revalidateTagupdateTagの主な違い:

  • updateTag:Server Actionのみで使用可能、キャッシュを直ちに期限切れにする、読み取り後の自分の書き込み用
  • revalidateTag:Server ActionおよびRoute Handlerで使用可能、profile="max"でstale-while-revalidateをサポート

詳細は、updateTag APIリファレンスをご覧ください。