Data Fetching & Caching
Data Fetching & Caching
In the App Router, data fetching happens directly in server components — no useEffect, no loading state, no separate API call. The component async/awaits the data and renders it server-side.
Next.js extends the native fetch API with caching options, and provides React.cache() for per-request deduplication.
The Three Fetching Strategies
1. Static (SSG) — build once, serve forever
// app/courses/page.tsx
// Fetches at BUILD TIME — result cached forever
const res = await fetch('https://api.example.com/courses', {
cache: 'force-cache'
})
const courses = await res.json()Fastest possible delivery — the HTML is pre-generated and served from CDN. Use for content that rarely changes.
2. ISR — rebuild on a schedule
// Fetch with 60-second revalidation
const res = await fetch('https://api.example.com/courses', {
next: { revalidate: 60 }
})
// Or set revalidation at the page level
export const revalidate = 60 // all fetches in this page revalidate every 60s
export default async function CoursesPage() {
const courses = await getCourses()
return <CourseList courses={courses} />
}The page is served from cache. After 60 seconds, the next request triggers a silent background regeneration. Users always get a fast response; data stays fresh.
3. Dynamic (SSR) — fresh on every request
const res = await fetch('https://api.example.com/user/profile', {
cache: 'no-store' // bypass cache entirely — always fresh
})Use for user-specific data — personalized content, real-time balances, private records that must never be cached across users.
generateStaticParams — Pre-render Dynamic Routes
// app/courses/[id]/page.tsx
export async function generateStaticParams() {
const courses = await getCourses()
// Returns all the [id] values to pre-generate at build time
return courses.map(c => ({ id: c.id }))
}
export default async function CoursePage({
params
}: {
params: Promise<{ id: string }>
}) {
const { id } = await params
const course = await getCourse(id)
if (!course) return notFound()
return <CourseDetail course={course} />
}At build time, Next.js calls generateStaticParams, gets the list of IDs, and generates a static HTML page for each one. Visiting /courses/cc-react-m01 serves pre-built HTML instantly.
React cache() — Per-Request Deduplication
import { cache } from 'react'
import { COURSES } from '@/lib/courses'
// Wrapping getCourse in cache() deduplicates calls within one request
export const getCourse = cache(async (id: string) => {
// In a real app, this would be a database query
return COURSES.find(c => c.id === id) ?? null
})
// app/courses/[id]/page.tsx — calls getCourse(id) for the page
// app/courses/[id]/layout.tsx — also calls getCourse(id) for the breadcrumb
// Result: getCourse() executes once — both components share the resultWithout cache(), two server components calling getCourse('cc-react-m01') in the same request would hit the database twice. With cache(), the second call returns the memoized result.