データ取得、キャッシュ、再検証
データ取得はあらゆるアプリケーションのコア機能です。このページでは、ReactとNext.jsでデータを取得、キャッシュ、再検証する方法を説明します。
データ取得には4つの方法があります:
サーバーサイドでの fetch
を使用したデータ取得
Next.jsはネイティブのfetch
Web APIを拡張し、サーバーサイドでの各fetchリクエストに対してキャッシュと再検証の動作を設定可能にしています。Reactはfetch
を拡張し、Reactコンポーネントツリーのレンダリング中にfetchリクエストを自動的にメモ化します。
fetch
はServer Components、Route Handlers、Server Actionsでasync
/await
と共に使用できます。
例:
async function getData() {
const res = await fetch('https://api.example.com/...')
// 戻り値はシリアライズされません
// Date、Map、Setなどを返せます
if (!res.ok) {
// 最も近い`error.js`エラーバウンダリを起動します
throw new Error('データの取得に失敗しました')
}
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/...')
// 戻り値はシリアライズされません
// Date、Map、Setなどを返せます
if (!res.ok) {
// 最も近い`error.js`エラーバウンダリを起動します
throw new Error('データの取得に失敗しました')
}
return res.json()
}
export default async function Page() {
const data = await getData()
return <main></main>
}
知っておくと良いこと:
- Next.jsはServer Componentsでデータを取得する際に便利な
cookies
やheaders
といった関数を提供しています。これらはリクエスト時の情報に依存するため、ルートを動的にレンダリングさせます。- Route Handlersでは、fetchリクエストはメモ化されません。Route HandlersはReactコンポーネントツリーの一部ではないためです。
- Server Actionsでは、fetchリクエストはキャッシュされません(デフォルトは
cache: no-store
)。- TypeScriptでServer Components内で
async
/await
を使用するには、TypeScript5.1.3
以上と@types/react
18.2.8
以上が必要です。
データのキャッシュ
キャッシュはデータを保存し、リクエストごとにデータソースから再取得する必要をなくします。
デフォルトでは、Next.jsはサーバー上のData Cacheにfetch
の戻り値を自動的にキャッシュします。これはデータがビルド時またはリクエスト時に取得され、キャッシュされ、各データリクエストで再利用されることを意味します。
// 'force-cache'はデフォルトで省略可能
fetch('https://...', { cache: 'force-cache' })
ただし例外もあり、fetch
リクエストは以下の場合キャッシュされません:
- Server Action内で使用された場合
POST
メソッドを使用するRoute Handler内で使用された場合
Data Cacheとは?
Data Cacheは永続的なHTTPキャッシュです。プラットフォームに応じて、キャッシュは自動的にスケールし、複数リージョン間で共有可能です。
Data Cacheについて詳しく学びます。
データの再検証
再検証はData Cacheをクリアし、最新のデータを再取得するプロセスです。データが変更され、最新情報を表示したい場合に便利です。
キャッシュされたデータは2つの方法で再検証できます:
- 時間ベースの再検証:一定時間が経過した後、自動的にデータを再検証します。頻繁に変更されず、鮮度が重要でないデータに有用です。
- オンデマンド再検証:イベント(例:フォーム送信)に基づいて手動でデータを再検証します。オンデマンド再検証ではタグベースまたはパスベースのアプローチを使用して、一度にデータのグループを再検証できます。ヘッドレスCMSのコンテンツが更新されたときなど、可能な限り早く最新データを表示したい場合に有用です。
時間ベースの再検証
一定間隔でデータを再検証するには、fetch
のnext.revalidate
オプションを使用してリソースのキャッシュ有効期間(秒単位)を設定します。
fetch('https://...', { next: { revalidate: 3600 } })
または、ルートセグメント内のすべてのfetch
リクエストを再検証するには、Segment Config Optionsを使用できます。
export const revalidate = 3600 // 最大1時間ごとに再検証
静的レンダリングされたルートに複数のfetchリクエストがあり、それぞれ異なる再検証頻度が設定されている場合、すべてのリクエストに対して最も短い時間が使用されます。動的レンダリングされたルートでは、各fetch
リクエストが独立して再検証されます。
時間ベースの再検証について詳しく学びます。
オンデマンド再検証
データはServer ActionまたはRoute Handler内でパス(revalidatePath
)またはキャッシュタグ(revalidateTag
)によってオンデマンドで再検証できます。
Next.jsにはルート間でfetch
リクエストを無効化するキャッシュタグシステムがあります。
fetch
を使用する際、1つ以上のタグでキャッシュエントリにタグ付けできます。- その後、
revalidateTag
を呼び出してそのタグに関連付けられたすべてのエントリを再検証できます。
例えば、以下のfetch
リクエストはキャッシュタグ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()
// ...
}
その後、Server ActionでrevalidateTag
を呼び出してcollection
タグが付けられたこのfetch
呼び出しを再検証できます:
'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')
}
オンデマンド再検証について詳しく学びます。
エラーハンドリングと再検証
データの再検証中にエラーが発生した場合、最後に正常に生成されたデータがキャッシュから提供され続けます。次回のリクエストで、Next.jsはデータの再検証を再試行します。
データキャッシュのオプトアウト
fetch
リクエストは以下の場合キャッシュされません:
fetch
リクエストにcache: 'no-store'
が追加されている場合- 個々の
fetch
リクエストにrevalidate: 0
オプションが追加されている場合 POST
メソッドを使用するRouter Handler内でfetch
リクエストが行われた場合headers
またはcookies
使用後にfetch
リクエストが行われた場合const dynamic = 'force-dynamic'
ルートセグメントオプションが使用されている場合fetchCache
ルートセグメントオプションがデフォルトでキャッシュをスキップするように設定されている場合fetch
リクエストがAuthorization
またはCookie
ヘッダーを使用し、コンポーネントツリー内にその上の未キャッシュリクエストがある場合
個々のfetch
リクエスト
個々のfetch
リクエストでキャッシュをオプトアウトするには、fetch
のcache
オプションを'no-store'
に設定します。これにより、リクエストごとに動的にデータが取得されます。
fetch('https://...', { cache: 'no-store' })
利用可能なすべてのcache
オプションはfetch
APIリファレンスで確認できます。
複数のfetch
リクエスト
ルートセグメント(例:LayoutやPage)に複数のfetch
リクエストがある場合、Segment Config Optionsを使用してセグメント内のすべてのデータリクエストのキャッシュ動作を設定できます。
ただし、各fetch
リクエストのキャッシュ動作を個別に設定することを推奨します。これにより、キャッシュ動作をより細かく制御できます。
サーバーサイドでのサードパーティライブラリを使用したデータ取得
fetch
をサポートまたは公開していないサードパーティライブラリ(データベース、CMS、ORMクライアントなど)を使用する場合、Route Segment Config OptionとReactのcache
関数を使用して、それらのリクエストのキャッシュと再検証の動作を設定できます。
データがキャッシュされるかどうかは、ルートセグメントが静的または動的にレンダリングされるかによります。セグメントが静的(デフォルト)の場合、リクエストの出力はルートセグメントの一部としてキャッシュされ、再検証されます。セグメントが動的な場合、リクエストの出力はキャッシュされず、セグメントがレンダリングされるたびにリクエストごとに再取得されます。
実験的なunstable_cache
APIも使用できます。
例
以下の例では:
- Reactの
cache
関数を使用してデータリクエストをメモ化しています。 - LayoutとPageセグメントで
revalidate
オプションを3600
に設定し、データが最大1時間ごとにキャッシュおよび再検証されます。
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
関数が2回呼び出されても、データベースへのクエリは1回だけ実行されます。
import { getItem } from '@/utils/get-item'
export const revalidate = 3600 // データを最大1時間ごとに再検証
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 // データを最大1時間ごとに再検証
export default async function Layout({ params: { id } }) {
const item = await getItem(id)
// ...
}
import { getItem } from '@/utils/get-item'
export const revalidate = 3600 // データを最大1時間ごとに再検証
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 // データを最大1時間ごとに再検証
export default async function Page({ params: { id } }) {
const item = await getItem(id)
// ...
}
クライアントサイドでのRoute Handlersを使用したデータ取得
クライアントコンポーネントでデータを取得する必要がある場合、クライアントからRoute Handlerを呼び出せます。Route Handlersはサーバー上で実行され、データをクライアントに返します。APIトークンなどの機密情報をクライアントに公開したくない場合に有用です。
例についてはRoute Handlerドキュメントを参照してください。
Server ComponentsとRoute Handlers
Server Componentsはサーバー上でレンダリングされるため、Server Componentからデータを取得するためにRoute Handlerを呼び出す必要はありません。代わりに、Server Component内で直接データを取得できます。
クライアントサイドでのサードパーティライブラリを使用したデータ取得
SWRやTanStack Queryなどのサードパーティライブラリを使用してクライアントサイドでデータを取得することもできます。これらのライブラリは、リクエストのメモ化、キャッシュ、再検証、データ変更のための独自のAPIを提供します。
将来のAPI:
use
は関数から返されたPromiseを受け入れ処理するReact関数です。現在、クライアントコンポーネントでfetch
をuse
でラップすることは推奨されず、複数の再レンダリングを引き起こす可能性があります。use
についてReactドキュメントで詳しく学びます。