Data Fetching, Caching, and Revalidating
データの取得は、あらゆるアプリケーションの中心的な部分です。このページでは、React と Next.js でデータを fetch、cache、そして revalidate する方法について説明しています。
データを fetch するための 4 つの方法があります:
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 Actionsでasync
/await
と一緒に使用することができます。
For example:
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>
}
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 は、
cookies
やheaders
など、Server Components でデータを取得する際に必要となる便利な機能を提供します。これらは、request 時の情報に依存するため、route が動的にレンダリングされます。- Route ハンドラーでは、
fetch
Request はメモ化されません。なぜなら、Route ハンドラーは React component ツリーの一部ではないからです。async
/await
を Server Component の中で TypeScript と共に使用するには、TypeScript5.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' });
fetch
Request はPOST
method を使用する完全に自動的にキャッシュされます。それがPOST
method を使用するRoute Handlerの中にある場合を除き、キャッシュされません。
データキャッシュとは何ですか?
データ cache は永続的なHTTP キャッシュ です。プラットフォームによっては、cache は自動的にスケールアップし、複数の地域で共有 することができます。
Data Cacheについてもっと学びましょう。
データの再検証
Revalidation は、データ Cache をパージし、最新のデータを再取得するプロセスです。これは、データが変更され、最新の情報を表示したい場合に役立ちます。
キャッシュデータは、2 つの方法で再検証することができます:
- 時間に基づく再検証: 一定の時間が経過した後にデータを自動的に revalidate します。これは、データがあまり頻繁に変更されず、新鮮さがそれほど重要ではない場合に役立ちます。
- オンデマンドリバリデーション: イベント(例:フォームの送信)に基づいてデータを手動で revalidate します。オンデマンドリバリデーションは、一度にデータのグループを revalidate するためにタグベースまたはパスベースのアプローチを使用することができます。これは、最新のデータができるだけ早く表示されることを確認したい場合(例:ヘッドレス CMS からのコンテンツが更新されたとき)に役立ちます。
時間ベースの再検証
データを一定期間で revalidate するには、リソースの cache 寿命(秒単位)を設定するために、fetch
のnext.revalidate
オプションを使用することができます。
fetch('https://...', { next: { revalidate: 3600 } });
あるいは、fetch
の全リクエストを revalidate するために、特定の route セグメントで、セグメント Config Optionsを使用することができます。
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 は、 fetch
Request を routes 全体で無効化するための cache タグ付けシステムを持っています。
fetch
を使用する際には、cache エントリに 1 つ以上のタグを付けるオプションがあります。- 次に、そのタグに関連付けられたすべてのエントリを revalidate するために、
revalidateTag
を呼び出すことができます。
例えば、以下のfetch
request は、cache タグのcollection
を追加します:
export default async function Page() {
const res = await fetch('https://...', { next: { tags: ['collection'] } })
const data = await res.json()
// ...
}
export default async function Page() {
const res = await fetch('https://...', { next: { tags: ['collection'] } })
const data = await res.json()
// ...
}
その後、この fetch
コールを collection
でタグ付けして、 Server Action 内で revalidateTag
を呼び出すことで、revalidate することができます。
'use server'
import { revalidateTag } from 'next/cache'
export default async function action() {
revalidateTag('collection')
}
'use server'
import { revalidateTag } from 'next/cache'
export default async function action() {
revalidateTag('collection')
}
on-demand revalidationについてもっと詳しく学びましょう。
Error ハンドリングと再検証
データを revalidate しようとする間に error がスローされた場合、最後に成功したデータは cache から提供され続けます。次の request では、Next.js はデータの再検証を再試行します。
データキャッシングのオプトアウト
fetch
Request は以下の場合にキャッシュされません:
fetch
Request にcache: 'no-store'
が追加されます。revalidate: 0
オプションが個々のfetch
Request に追加されました。fetch
request は、POST
method を使用する Router Handler の中にあります。fetch
request は、headers
やcookies
の使用の後に来ます。const dynamic = 'force-dynamic'
route セグメントオプションが使用されます。fetchCache
の route セグメントオプションは、default によって cache をスキップするように設定されています。fetch
request は、Authorization
またはCookie
headers を使用し、その上に component ツリーの中でキャッシュされていない request があります。
個々のfetch
リクエスト
個々のfetch
リクエストについてキャッシュをオプトアウトするには、fetch
のcache
オプションを'no-store'
に設定できます。これにより、すべての request ごとにデータが動的に fetch されます。
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
に設定されており、これはデータがキャッシュされ、最大で毎時再検証されることを意味します。
import { cache } from 'react'
export const getItem = cache(async (id: string) => {
const item = await db.item.findUnique({ id })
return item
})
import { cache } from 'react'
export const getItem = cache(async (id) => {
const item = await db.item.findUnique({ id })
return item
})
getItem
関数は二回呼び出されますが、データベースには一つだけの query が行われます。
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)
// ...
}
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)
// ...
}
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)
// ...
}
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 の関数です。fetch
をuse
でラップすることは、現在 Client Components では推奨されていませんし、複数の再レンダリングを引き起こす可能性があります。React のドキュメンテーション でuse
についてさらに学びましょう。