Foundations
Route handlers & data
Lesson 3 of 3
What you'll learn
- Write a route handler that returns JSON
- Know when you need a handler vs. just fetching in a Server Component
- Classify data as static, cached, or dynamic
Most data you can read directly inside a Server Component. But when you need a real endpoint — something the browser, a webhook, or another service calls — you use a route handler: a function in app/.../route.ts that receives a Request and returns a Response.
// app/api/tutor/route.ts
export async function POST(request: Request) {
const { question } = await request.json();
return Response.json({ answer: `Think of "${question}" as a boundary.` });
}
That's the clean place to stub an integration (an AI tutor, an avatar render, a webhook) before the real provider is wired — the contract is fixed, the implementation swaps later.
Static, cached, or dynamic
Every read is one of three things, and choosing well is most of performance:
- Static — same for everyone, rarely changes (a course title). Render once.
- Cached — shared but refreshable (a published catalog). Cache with a revalidate window.
- Dynamic — per-user or live (a learner's progress, a tutor reply). Compute every request.
Never cache identity
Cache stable knowledge; refresh personal state. Caching a user's progress is how one learner sees another's dashboard. When in doubt, personal data is dynamic.
The challenge classifies reads and routes each to the right strategy. Run it.
Run it. Shared+stable → cached; per-user or conversational → dynamic.
You can now read an App Router codebase, place chrome correctly, and reason about server boundaries and freshness — the foundation everything else builds on.
Sign in to save your progress across devices.