BuildBot

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.

Pick a caching strategy

Run it. Shared+stable → cached; per-user or conversational → dynamic.

Loading editor…

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.