GET caching in Route Handlers and how to opt out
Tests App Router GET Route Handler caching and dynamic opt-outs. Hit: export const dynamic = 'force-dynamic', revalidate = 0, dynamic APIs like cookies(), and unstable_noStore(). Red flag: confusing this with Pages Router API routes, dynamic by default.
WHAT THIS TESTS: Whether you understand that App Router GET Route Handlers are statically generated by default and can articulate the exact mechanisms to force per-request dynamic execution. Interviewers want to see you know the difference between Route Handlers and Pages Router API routes, and that you can name concrete APIs rather than hand-waving about cache headers.
A GOOD ANSWER COVERS: First, state that GET Route Handlers are cached by default at build time unless the runtime detects dynamic behavior or explicit opt-out configuration. Second, list the segment config options: export const dynamic = 'force-dynamic' forces dynamic rendering on every request, and export const revalidate = 0 treats the route as uncached. Third, explain that calling dynamic APIs inside the handler implicitly opts out of static caching; these include cookies(), headers(), and draftMode() from next/headers, plus connection() from next/server. Fourth, mention unstable_noStore() imported from next/cache as a programmatic way to declare that a route should not be cached. Fifth, note that non-GET methods like POST, PUT, PATCH, and DELETE are never cached by default.
COMMON WRONG ANSWERS: Confusing Route Handlers with Pages Router API routes, which are serverless functions that run dynamically by default. Suggesting that you opt out by manually setting Cache-Control headers; while headers affect downstream caching, they are not the Next.js mechanism for opting out of static generation. Claiming that fetch with cache no-store is sufficient; that controls fetch deduping and caching but does not necessarily mark the entire route handler as dynamic. Forgetting that simply reading cookies() or headers() inside the handler automatically forces dynamic execution without any extra config.
LIKELY FOLLOW-UPS: How would you revalidate a cached route handler after a data mutation? The answer is revalidateTag or revalidatePath. What is the difference between dynamic = 'force-dynamic' and unstable_noStore? The former forces dynamic rendering for the whole route, while the latter is a granular opt-out that can be used in specific code paths. Does a Route Handler respect fetch cache settings? Yes, but route-level config overrides fetch-level behavior for static generation decisions.
ONE CONCRETE EXAMPLE: Imagine a GET handler at app/api/dashboard/route.ts that returns the current server time. If you write a basic Response.json({ now: Date.now() }), it returns the build timestamp on every request because the route is statically cached. To fix it, you can add export const dynamic = 'force-dynamic' at the top of the file, or call headers() inside the handler to force dynamic execution, or call unstable_noStore() before generating the response. Any of these makes Date.now() evaluate per request.
Read the original → nextjs.org
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.