データの取得
このページでは、Server Component と Client Component でどのようにデータを取得できるか、またデータに依存するコンポーネントをどのようにストリーミングできるかについて説明します。
データの取得
サーバーコンポーネント
Server Component では、以下を使用してデータを取得できます。
fetch API を使用する
fetch API でデータを取得するには、コンポーネントを非同期関数に変換し、fetch 呼び出しを await します。たとえば、
export default async function Page() {
const data = await fetch('https://api.vercel.app/blog')
const posts = await data.json()
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}知っておくと良いこと
fetch応答はデフォルトではキャッシュされません。ただし、Next.js はルートをプリレンダリングし、パフォーマンス向上のために出力をキャッシュします。 動的レンダリングを採用したい場合は、{ cache: 'no-store' }オプションを使用してください。fetchAPI リファレンスを参照してください。- 開発中は、
fetch呼び出しをログに記録することで、可視性とデバッグを向上させることができます。loggingAPI リファレンスを参照してください。
ORM またはデータベースを使用する
Server Component はサーバーでレンダリングされるため、ORM またはデータベースクライアントを使用して安全にデータベースクエリを実行できます。コンポーネントを非同期関数に変換し、呼び出しを await します。
import { db, posts } from '@/lib/db'
export default async function Page() {
const allPosts = await db.select().from(posts)
return (
<ul>
{allPosts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}クライアントコンポーネント
Client Component でデータを取得するには、次の 2 つの方法があります。
- React の
useフック - SWR や React Query などのコミュニティライブラリ
use フックによるストリーミングデータ
React の use フック を使用して、サーバーからクライアントへデータをストリーミングできます。まず Server Component でデータを取得し、その Promise を Client Component に props として渡します。
import Posts from '@/app/ui/posts'
import { Suspense } from 'react'
export default function Page() {
// Don't await the data fetching function
const posts = getPosts()
return (
<Suspense fallback={<div>Loading...</div>}>
<Posts posts={posts} />
</Suspense>
)
}次に、Client Component で use フックを使用して Promise を読み取ります。
'use client'
import { use } from 'react'
export default function Posts({
posts,
}: {
posts: Promise<{ id: string; title: string }[]>
}) {
const allPosts = use(posts)
return (
<ul>
{allPosts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}上記の例では、<Posts> コンポーネントは <Suspense> 境界 でラップされています。これは、Promise が解決される間、フォールバックが表示されることを意味します。 ストリーミングの詳細についてはこちらをご覧ください。
コミュニティライブラリ
Client Component でデータを取得するには、SWR や React Query などのコミュニティライブラリを使用できます。これらのライブラリには、キャッシング、ストリーミングなどの機能に関する独自のセマンティクスがあります。たとえば、SWR の場合
'use client'
import useSWR from 'swr'
const fetcher = (url) => fetch(url).then((r) => r.json())
export default function BlogPage() {
const { data, error, isLoading } = useSWR(
'https://api.vercel.app/blog',
fetcher
)
if (isLoading) return <div>Loading...</div>
if (error) return <div>Error: {error.message}</div>
return (
<ul>
{data.map((post: { id: string; title: string }) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}リクエストの重複排除とデータキャッシュ
fetch リクエストの重複を排除する 1 つの方法は、リクエストのメモ化を使用することです。このメカニズムにより、単一のレンダリングパスで同じ URL とオプションを使用する GET または HEAD の fetch 呼び出しは、1 つのリクエストに結合されます。これは自動的に行われ、fetch に AbortSignal を渡すことでオプトアウトできます。
リクエストのメモ化は、リクエストの有効期間にスコープされます。
Next.js の データキャッシュを使用して fetch リクエストの重複を排除することもできます。たとえば、fetch オプションで cache: 'force-cache' を設定することで実現できます。
データキャッシュにより、現在のレンダリングパスと着信リクエスト間でデータを共有できます。
fetch を使用しておらず、代わりに ORM またはデータベースを直接使用している場合は、データアクセスを React の cache 関数でラップできます。
import { cache } from 'react'
import { db, posts, eq } from '@/lib/db'
export const getPost = cache(async (id: string) => {
const post = await db.query.posts.findFirst({
where: eq(posts.id, parseInt(id)),
})
})ストリーミング
注意:以下のコンテンツは、アプリケーションで
cacheComponents設定オプションが有効になっていることを前提としています。このフラグは Next.js 15 canary で導入されました。
Server Component でデータを取得すると、データはリクエストごとにサーバーで取得およびレンダリングされます。データリクエストが遅い場合、すべてのデータが取得されるまでルート全体のレンダリングがブロックされます。
初期ロード時間とユーザーエクスペリエンスを向上させるために、ストリーミングを使用してページの HTML を小さなチャンクに分割し、それらのチャンクをサーバーからクライアントに段階的に送信できます。

アプリケーションでストリーミングを実装するには、次の 2 つの方法があります。
loading.jsファイルでページをラップする<Suspense>でコンポーネントをラップする
loading.js を使用する
ページと同じフォルダに loading.js ファイルを作成すると、データが取得されている間、ページ全体をストリーミングできます。たとえば、app/blog/page.js をストリーミングするには、app/blog フォルダ内にファイルを追加します。

export default function Loading() {
// Define the Loading UI here
return <div>Loading...</div>
}ナビゲーション時に、ユーザーはレイアウトとローディング状態をすぐに確認でき、その間、ページはレンダリングされます。レンダリングが完了すると、新しいコンテンツが自動的に置き換えられます。

内部的には、loading.js は layout.js の中にネストされ、page.js ファイルとそれ以下のすべての子を <Suspense> 境界で自動的にラップします。

このアプローチはルートセグメント(レイアウトとページ)には適していますが、より詳細なストリーミングには <Suspense> を使用できます。
<Suspense> を使用する
<Suspense> を使用すると、ページのどの部分をストリーミングするかをより細かく制御できます。たとえば、<Suspense> 境界の外側にあるページコンテンツはすぐに表示し、境界内のブログ記事のリストはストリーミングできます。
import { Suspense } from 'react'
import BlogList from '@/components/BlogList'
import BlogListSkeleton from '@/components/BlogListSkeleton'
export default function BlogPage() {
return (
<div>
{/* This content will be sent to the client immediately */}
<header>
<h1>Welcome to the Blog</h1>
<p>Read the latest posts below.</p>
</header>
<main>
{/* Any content wrapped in a <Suspense> boundary will be streamed */}
<Suspense fallback={<BlogListSkeleton />}>
<BlogList />
</Suspense>
</main>
</div>
)
}意味のあるローディング状態の作成
インスタントローディング状態は、ナビゲーション後にすぐにユーザーに表示されるフォールバック UI です。最適なユーザーエクスペリエンスのために、アプリが応答していることをユーザーに理解させるのに役立つ、意味のあるローディング状態を設計することをお勧めします。たとえば、スケルトンやスピナー、またはカバー写真やタイトルなど、将来の画面の小さくも意味のある部分を使用できます。
開発中は、React Devtools を使用して、コンポーネントのローディング状態をプレビューおよび検査できます。
例
シーケンシャルなデータ取得
シーケンシャルなデータ取得は、ツリー内のネストされたコンポーネントがそれぞれ独自のデータを取得し、リクエストが重複排除されない場合に発生し、応答時間の遅延につながります。

このパターンが必要な場合があります。それは、あるフェッチが別のフェッチの結果に依存する場合です。
たとえば、<Playlists> コンポーネントは、<Playlists> が artistID prop に依存しているため、<Artist> コンポーネントがデータの取得を完了するまで、データの取得を開始しません。
export default async function Page({
params,
}: {
params: Promise<{ username: string }>
}) {
const { username } = await params
// Get artist information
const artist = await getArtist(username)
return (
<>
<h1>{artist.name}</h1>
{/* Show fallback UI while the Playlists component is loading */}
<Suspense fallback={<div>Loading...</div>}>
{/* Pass the artist ID to the Playlists component */}
<Playlists artistID={artist.id} />
</Suspense>
</>
)
}
async function Playlists({ artistID }: { artistID: string }) {
// Use the artist ID to fetch playlists
const playlists = await getArtistPlaylists(artistID)
return (
<ul>
{playlists.map((playlist) => (
<li key={playlist.id}>{playlist.name}</li>
))}
</ul>
)
}ユーザーエクスペリエンスを向上させるには、React の <Suspense> を使用して、データ取得中に fallback を表示する必要があります。これにより、ストリーミングが有効になり、シーケンシャルなデータリクエストによってルート全体がブロックされるのを防ぎます。
パラレルなデータ取得
パラレルなデータ取得は、ルート内のデータリクエストが早期に開始され、同時に開始される場合に発生します。
デフォルトでは、レイアウトとページはパラレルにレンダリングされます。そのため、各セグメントは可能な限り早くデータの取得を開始します。
ただし、任意のコンポーネント内でも、複数の async/await リクエストは、互いの後に配置されている場合、シーケンシャルになる可能性があります。たとえば、getArtist が解決されるまで getAlbums はブロックされます。
import { getArtist, getAlbums } from '@/app/lib/data'
export default async function Page({ params }) {
// These requests will be sequential
const { username } = await params
const artist = await getArtist(username)
const albums = await getAlbums(username)
return <div>{artist.name}</div>
}fetch を呼び出して複数のリクエストを開始し、その後 Promise.all でそれらを await します。リクエストは fetch が呼び出されるとすぐに開始されます。
import Albums from './albums'
async function getArtist(username: string) {
const res = await fetch(`https://api.example.com/artist/${username}`)
return res.json()
}
async function getAlbums(username: string) {
const res = await fetch(`https://api.example.com/artist/${username}/albums`)
return res.json()
}
export default async function Page({
params,
}: {
params: Promise<{ username: string }>
}) {
const { username } = await params
// Initiate requests
const artistData = getArtist(username)
const albumsData = getAlbums(username)
const [artist, albums] = await Promise.all([artistData, albumsData])
return (
<>
<h1>{artist.name}</h1>
<Albums list={albums} />
</>
)
}知っておくと便利:
Promise.allを使用しているときに 1 つのリクエストが失敗すると、操作全体が失敗します。これを処理するには、代わりにPromise.allSettledメソッドを使用できます。
データのプリロード
ユーティリティ関数を作成してデータをプリロードできます。この関数は、ブロッキングリクエストの上に早期に呼び出されます。<Item> は checkIsAvailable() 関数に基づいて条件付きでレンダリングされます。
<Item/> がレンダリングされるまでにデータがすでにフェッチされているように、checkIsAvailable() の前に preload() を呼び出すことができます。
import { getItem, checkIsAvailable } from '@/lib/data'
export default async function Page({
params,
}: {
params: Promise<{ id: string }>
}) {
const { id } = await params
// starting loading item data
preload(id)
// perform another asynchronous task
const isAvailable = await checkIsAvailable()
return isAvailable ? <Item id={id} /> : null
}
export const preload = (id: string) => {
// void evaluates the given expression and returns undefined
// https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/void
void getItem(id)
}
export async function Item({ id }: { id: string }) {
const result = await getItem(id)
// ...
}さらに、React の cache 関数 と server-only パッケージ を使用して、再利用可能なユーティリティ関数を作成できます。このアプローチにより、データ取得関数をキャッシュし、サーバーでのみ実行されるようにすることができます。
import { cache } from 'react'
import 'server-only'
import { getItem } from '@/lib/data'
export const preload = (id: string) => {
void getItem(id)
}
export const getItem = cache(async (id: string) => {
// ...
})役に立ちましたか?




