Next.js Data Fetching
Fetch data in Server Components with async/await, revalidation, and request deduping
Overview
In the App Router you fetch data directly inside async Server Components using fetch or your database client. Next.js extends fetch with caching and revalidation options, and automatically dedupes identical requests during a render. This keeps data access close to where it is used and avoids client-side waterfalls.
Syntax / Usage
Make the component async and await your data. The next option controls caching and revalidation.
// app/dashboard/page.tsx
export default async function Dashboard() {
const res = await fetch('https://api.example.com/stats', {
next: { revalidate: 3600 }, // revalidate at most once per hour
})
const stats = await res.json()
return <StatsGrid stats={stats} />
}
// Opt out of caching for always-fresh data
const res = await fetch('https://api.example.com/live', {
cache: 'no-store',
})
Examples
Fetch in parallel to avoid sequential waterfalls:
export default async function ProfilePage({ params }) {
const { id } = await params
const [user, posts] = await Promise.all([
getUser(id),
getPosts(id),
])
return <Profile user={user} posts={posts} />
}
Fetch directly from a database in a Server Component:
import { db } from '@/lib/db'
export default async function Products() {
const products = await db.product.findMany({ orderBy: { name: 'asc' } })
return (
<ul>
{products.map((p) => <li key={p.id}>{p.name}</li>)}
</ul>
)
}
Common Mistakes
- Creating waterfalls by awaiting requests sequentially instead of using
Promise.all - Using
useEffectto fetch in Client Components when a Server Component would do - Forgetting
cache: 'no-store'for personalized or real-time data - Assuming database calls are deduped like
fetch— they are not unless wrapped incache() - Passing non-serializable fetched values across the server/client boundary
See Also
nextjs-caching nextjs-streaming-suspense server-components