Static vs Dynamic Handlers
The phone app calls GET /api/trails every time it opens: thousands of times a day. The trail list changes maybe three times a day. Running the handler and querying the database for every one of those calls repeats the same work to produce the same answer. Pages can be static; so can endpoints.
The rule
Handlers run per request by default; opt a
GETinto static when everyone gets the same answer.
In current Next.js, a GET route handler is dynamic by default: it runs on every request. That is the safe choice, because a handler might read cookies, headers or the query string.
When a GET handler's answer is the same for everyone, opt it into static with route segment config:
// app/api/trails/route.ts
export const dynamic = "force-static"; // generate once, serve the saved result
export const revalidate = 3600; // regenerate at most once an hour
export async function GET() {
const trails = await getTrails();
return Response.json(trails);
}Now next build runs the handler, saves the response, and serves that copy to every caller. With revalidate, the saved copy is refreshed in the background once it is older than an hour, just like a static page.
Three rules keep this safe:
- Only
GETcan be cached.POST,PUT,PATCHandDELETEchange things, so they always run. - Request-specific data does not mix with static. Under
force-static, reading cookies or headers gives empty values, because there is no real request at build time. A handler that depends on the caller must stay dynamic. - Dynamic is the default. You only add config when you decide caching is safe.
Newer releases also offer an opt-in "use cache" directive that expresses the same idea in a different way; the question you ask is identical.
The shortcut
For each endpoint ask "Would every caller get the same bytes?" and "Does it only read?" Two yeses: static, with a revalidate matched to how often the data changes. Any no: leave it dynamic.
GET /api/trails
Same list for everyone, read-only, changes a few times a day. Static with
revalidate = 3600.
The trap is making a handler that reads the session cookie force-static. Nothing errors, but every caller gets the same answer, built from an empty cookie at build time.
Drills
Answer fast — trust the rule.
By default in current Next.js, when does a GET route handler run?
Which line opts a GET handler into being generated once and reused?
Which Trailhead handler can safely be static?
With force-static and revalidate = 600, what happens after ten minutes?
Remember
- Handlers run per request by default; opt a
GETinto static when everyone gets the same answer. - Shortcut: same bytes for every caller, and read-only? Then static with a sensible
revalidate. - Trap: request-specific handlers must stay dynamic, and non-
GETmethods are never cached.
