How does data fetching work in the App Router? What are the fetch cache options?
Take a moment.
Say your answer out loud or write it down first — retrieval is what makes it stick. Reveal when you're ready.
Short answer
Server Components call fetch directly (or query DBs/ORMs). fetch gains cache options: cache: 'force-cache' pins snapshots, 'no-store' opts out, next.revalidate sets TTL refresh, next.tags enable targeted invalidation via revalidateTag().
Detailed explanation
App Router collapses data-layer complexity into plain async code: await fetch() or ORM calls right inside Server Components — no effect lifecycle, no loading prop plumbing. Caching rides on fetch itself: cached GET responses serve instantly; revalidate: 60 serves snapshots up to 60 seconds then refreshes in the background (ISR semantics); tags label entries so mutations elsewhere call revalidateTag('products') to purge precisely. Non-fetch data participates via route segment config (revalidate, dynamic exports) or the unstable_cache wrapper. Reading request-time data — cookies(), headers() — automatically flips routes dynamic. Client-side fetching remains normal React patterns via SWR/TanStack Query hitting Route Handlers. Recent versions flipped the default toward no-store, requiring explicit opt-in for caching — knowing which default your version uses is a strong currency signal in interviews.
Example
// Cached 60s, tagged for targeted invalidation:
const res = await fetch(`${API}/products`, {
next: { revalidate: 60, tags: ['products'] },
});
// Somewhere handling a mutation:
import { revalidateTag } from 'next/cache';
revalidateTag('products'); // only product queries refetchInterview tip
Walk one realistic flow: cached fetch with tag → mutation → revalidateTag. That demonstrates the whole mental model in three lines.
Common mistakes
- Assuming every fetch is cached by default regardless of version.
- Forgetting cookies()/headers() usage makes routes dynamic automatically.