Skip to the solution
MediumFrontend5 min read

What are Route Handlers in Next.js and how do they differ from Pages API routes?

496 views

The problem

Question presented to candidate: "You are moving /pages/api endpoints to the App Router. What are Route Handlers, how is the code different, and what do you need to watch for?"

What a strong answer should cover:

  • A Route Handler is a route.ts file in app/ that exports functions named after HTTP methods (GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS).
  • It uses the Web standard Request and Response (plus NextRequest and NextResponse helpers), not Node-style req and res.
  • Unexported methods return 405 automatically; dynamic segments arrive as an awaited params Promise in the second argument.
  • GET handlers are dynamic by default since Next.js 15; without Cache Components you opt into static output with export const dynamic = 'force-static'. With Cache Components (the create-next-app default in 16.4) that export is a build error: a GET is prerendered when it reads no request or uncached data, and 'use cache' caches the rest.
  • Pages API routes still work side by side; there is one default-exported handler(req, res) per file with a method switch.

Clarifying questions expected:

  • "Do any endpoints rely on Node-specific res methods or body parser config?" β€” they need rewriting.
  • "Should any GET endpoint be static?" β€” it now needs an explicit opt-in.

Code / implementation expected: Yes β€” the same endpoint in both styles.

  • nextjs
  • route-handlers
  • api
  • app-router

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

5 min read Β· 8 sections

Read the code

A Pages API route and its Route Handler version
// pages/api/items/[id].ts  (Pages Router)
import type { NextApiRequest, NextApiResponse } from "next";
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  if (req.method === "GET") return res.status(200).json(await getItem(String(req.query.id)));
  if (req.method === "DELETE") { await deleteItem(String(req.query.id)); return res.status(204).end(); }
  res.setHeader("Allow", "GET, DELETE").status(405).end();
}

// app/api/items/[id]/route.ts  (App Router)
export async function GET(_req: Request, ctx: { params: Promise<{ id: string }> }) {
  const { id } = await ctx.params;
  return Response.json(await getItem(id));
}
export async function DELETE(_req: Request, ctx: { params: Promise<{ id: string }> }) {
  const { id } = await ctx.params;
  await deleteItem(id);
  return new Response(null, { status: 204 });
}

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 76 of 129 in Next.js

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Next.js questions
Up nextMedium

What are the rendering strategies in Next.js (SSR, SSG, ISR, CSR)?

Continue