Lang x Lang

Data Fetching, Caching, and Revalidating

データの取得は、あらゆるアプリケーションの中心的な部分です。このページでは、React と Next.js でデータを fetch、cache、そして revalidate する方法について説明しています。

データを fetch するための 4 つの方法があります:

  1. fetchを使って server 上で
  2. server 上で、サードパーティのライブラリを利用して
  3. client 上で、Route ハンドラを使って
  4. client 上でのサードパーティライブラリ

Fetching Data on the Server with fetch

Next.js は、ネイティブの fetch Web API を拡張して、各 fetch request に対するキャッシング再検証の動作を server で設定できるようにします。 React は、fetchを拡張して、 React component ツリーのレンダリング中に自動的に fetch リクエストをメモ化します。

fetchは Server Components、Route Handlers、そしてServer Actionsasync/awaitと一緒に使用することができます。

For example:

app/page.tsx
async function getData() {
  const res = await fetch('https://api.example.com/...')
  // The return value is *not* serialized
  // You can return Date, Map, Set, etc.

  if (!res.ok) {
    // This will activate the closest `error.js` Error Boundary
    throw new Error('Failed to fetch data')
  }

  return res.json()
}

export default async function Page() {
  const data = await getData()

  return <main></main>
}
app/page.js
async function getData() {
  const res = await fetch('https://api.example.com/...')
  // The return value is *not* serialized
  // You can return Date, Map, Set, etc.

  if (!res.ok) {
    // This will activate the closest `error.js` Error Boundary
    throw new Error('Failed to fetch data')
  }

  return res.json()
}

export default async function Page() {
  const data = await getData()

  return <main></main>
}

Good to know:

  • Next.js は、cookiesheadersなど、Server Components でデータを取得する際に必要となる便利な機能を提供します。これらは、request 時の情報に依存するため、route が動的にレンダリングされます。
  • Route ハンドラーでは、fetchRequest はメモ化されません。なぜなら、Route ハンドラーは React component ツリーの一部ではないからです。
  • async/awaitを Server Component の中で TypeScript と共に使用するには、TypeScript 5.1.3以降、@types/react 18.2.8以降を使用する必要があります。

データキャッシング

Caching はデータを保存し、毎回の request でデータ source から再取得する必要がないようにします。

default では、Next.js は自動的にfetchの戻り values を server のデータ Cacheに Cache します。これは、データを build 時または request 時に取得し、Cache して、各データ request ごとに再利用できることを意味します。

// 'force-cache' is the default, and can be omitted
fetch('https://...', { cache: 'force-cache' });

fetchRequest はPOSTmethod を使用する完全に自動的にキャッシュされます。それがPOST method を使用するRoute Handlerの中にある場合を除き、キャッシュされません。

データキャッシュとは何ですか?

データ cache は永続的なHTTP キャッシュ です。プラットフォームによっては、cache は自動的にスケールアップし、複数の地域で共有 することができます。

Data Cacheについてもっと学びましょう。

データの再検証

Revalidation は、データ Cache をパージし、最新のデータを再取得するプロセスです。これは、データが変更され、最新の情報を表示したい場合に役立ちます。

キャッシュデータは、2 つの方法で再検証することができます:

  • 時間に基づく再検証: 一定の時間が経過した後にデータを自動的に revalidate します。これは、データがあまり頻繁に変更されず、新鮮さがそれほど重要ではない場合に役立ちます。
  • オンデマンドリバリデーション: イベント(例:フォームの送信)に基づいてデータを手動で revalidate します。オンデマンドリバリデーションは、一度にデータのグループを revalidate するためにタグベースまたはパスベースのアプローチを使用することができます。これは、最新のデータができるだけ早く表示されることを確認したい場合(例:ヘッドレス CMS からのコンテンツが更新されたとき)に役立ちます。

時間ベースの再検証

データを一定期間で revalidate するには、リソースの cache 寿命(秒単位)を設定するために、fetchnext.revalidateオプションを使用することができます。

fetch('https://...', { next: { revalidate: 3600 } });

あるいは、fetchの全リクエストを revalidate するために、特定の route セグメントで、セグメント Config Optionsを使用することができます。

layout.js
export const revalidate = 3600 // revalidate at most every hour

あなたが静的にレンダリングされた route 内で複数の fetch リクエストを持っていて、それぞれが異なる再検証頻度を持っている場合。すべてのリクエストに対して最短の時間が使用されます。動的にレンダリングされた routes では、各fetch request は独立して再検証されます。

time-based revalidationについてもっと学びましょう。

オンデマンド再検証

データは、 path (revalidatePath)または cache タグ(revalidateTag)によって、オンデマンドで再検証できます。これは、Server ActionまたはRoute Handler内部で行います。

Next.js は、 fetchRequest を routes 全体で無効化するための cache タグ付けシステムを持っています。

  1. fetchを使用する際には、cache エントリに 1 つ以上のタグを付けるオプションがあります。
  2. 次に、そのタグに関連付けられたすべてのエントリを revalidate するために、revalidateTagを呼び出すことができます。

例えば、以下のfetch request は、cache タグのcollectionを追加します:

app/page.tsx
export default async function Page() {
  const res = await fetch('https://...', { next: { tags: ['collection'] } })
  const data = await res.json()
  // ...
}
app/page.js
export default async function Page() {
  const res = await fetch('https://...', { next: { tags: ['collection'] } })
  const data = await res.json()
  // ...
}

その後、この fetch コールを collection でタグ付けして、 Server Action 内で revalidateTag を呼び出すことで、revalidate することができます。

app/actions.ts
'use server'

import { revalidateTag } from 'next/cache'

export default async function action() {
  revalidateTag('collection')
}
app/actions.js
'use server'

import { revalidateTag } from 'next/cache'

export default async function action() {
  revalidateTag('collection')
}

on-demand revalidationについてもっと詳しく学びましょう。

Error ハンドリングと再検証

データを revalidate しようとする間に error がスローされた場合、最後に成功したデータは cache から提供され続けます。次の request では、Next.js はデータの再検証を再試行します。

データキャッシングのオプトアウト

fetchRequest は以下の場合にキャッシュされません

  • fetch Request に cache: 'no-store' が追加されます。
  • revalidate: 0 オプションが個々の fetch Request に追加されました。
  • fetchrequest は、POST method を使用する Router Handler の中にあります。
  • fetch request は、headerscookiesの使用の後に来ます。
  • const dynamic = 'force-dynamic' route セグメントオプションが使用されます。
  • fetchCacheの route セグメントオプションは、default によって cache をスキップするように設定されています。
  • fetch request は、AuthorizationまたはCookie headers を使用し、その上に component ツリーの中でキャッシュされていない request があります。

個々のfetchリクエスト

個々のfetchリクエストについてキャッシュをオプトアウトするには、fetchcacheオプションを'no-store'に設定できます。これにより、すべての request ごとにデータが動的に fetch されます。

layout.js
fetch('https://...', { cache: 'no-store' })

利用可能なすべての cache options をfetch API referenceで確認してください。

複数のfetchリクエスト

複数の fetch Request が route セグメント(例えば、Layout または Page)にある場合、Segment Config Options を使用して、セグメント内のすべてのデータ Request のキャッシュ動作を設定することができます。

ただし、各fetch request のキャッシング動作を個別に設定することをお勧めします。これにより、キャッシング動作に対するより詳細な制御が可能になります。

Fetching data on the Server with third-party libraries

あなたがfetchをサポートまたは公開していないサードパーティの library(例えば、データベース、CMS、ORM の client など)を使用している場合でも、それらの Request のキャッシングと再検証の振る舞いをRoute Segment Config Optionと React のcache関数を使用して設定することができます。

データがキャッシュされるかどうかは、route セグメントが静的にまたは動的にレンダリングされるかどうかによります。 もしセグメントが静的(default)であれば、request の出力はキャッシュされ、route セグメントの一部として再検証されます。 もしセグメントが dynamic であれば、 request の出力はキャッシュされず、セグメントがレンダリングされるたびに毎回 request で再取得されます。

あなたは実験的なunstable_cache APIも使用できます。

Example

以下の例で:

  • React の cache 関数は、データ Request を memoize するために使用されます。
  • revalidateオプションは Layout と Page セグメントで3600に設定されており、これはデータがキャッシュされ、最大で毎時再検証されることを意味します。
app/utils.ts
import { cache } from 'react'

export const getItem = cache(async (id: string) => {
  const item = await db.item.findUnique({ id })
  return item
})
app/utils.js
import { cache } from 'react'

export const getItem = cache(async (id) => {
  const item = await db.item.findUnique({ id })
  return item
})

getItem関数は二回呼び出されますが、データベースには一つだけの query が行われます。

app/item/[id]/layout.tsx
import { getItem } from '@/utils/get-item'

export const revalidate = 3600 // revalidate the data at most every hour

export default async function Layout({
  params: { id },
}: {
  params: { id: string }
}) {
  const item = await getItem(id)
  // ...
}
app/item/[id]/layout.js
import { getItem } from '@/utils/get-item'

export const revalidate = 3600 // revalidate the data at most every hour

export default async function Layout({ params: { id } }) {
  const item = await getItem(id)
  // ...
}
app/item/[id]/page.tsx
import { getItem } from '@/utils/get-item'

export const revalidate = 3600 // revalidate the data at most every hour

export default async function Page({
  params: { id },
}: {
  params: { id: string }
}) {
  const item = await getItem(id)
  // ...
}
app/item/[id]/page.js
import { getItem } from '@/utils/get-item'

export const revalidate = 3600 // revalidate the data at most every hour

export default async function Page({ params: { id } }) {
  const item = await getItem(id)
  // ...
}

Fetching Data on the Client with Route Handlers

もし client component でデータを fetch する必要がある場合、client からRouteを呼び出すことができます。Route Handlers は server で実行され、データを client に返します。これは、API トークンのようなセンシティブな情報を client に公開したくない場合に便利です。

例のためにRoute Handlerのドキュメンテーションをご覧ください。

Server Components と Route ハンドラー

Server Components render は server 上でレンダリングされるため、Server Component から Route Handler を呼び出して fetch データを取得する必要はありません。代わりに、Server Component 内で直接データを fetch できます。

Fetching Data on the Client with third-party libraries

また、SWR TanStack Query といったサードパーティの library を使用して、client でデータを fetch することもできます。これらのライブラリは、Request のメモ化、キャッシュ化、再検証、データの変更などを行うための独自の API を提供しています。

未来の API:

useは、関数から返されるプロミスを受け入れて処理するReact の関数です。 fetchuseでラップすることは、現在 Client Components では推奨されていませんし、複数の再レンダリングを引き起こす可能性があります。React のドキュメンテーション useについてさらに学びましょう。

当社サイトでは、Cookie を使用しています。各規約をご確認の上ご利用ください:
Cookie Policy, Privacy Policy および Terms of Use