Next.js App Router uses Route Handlers (route.ts files exporting HTTP method functions like GET, POST) instead of Pages Router API routes — they use standard Web APIs (Request/Response), support streaming, run only on the server, and are the way to build API endpoints when Server Actions aren't appropriate.
route.ts files export named HTTP method functions (GET, POST, PUT, DELETE) using standard Web Request/Response APIs — not Node.js req/res.
GET handlers are cached by default when they don't read dynamic request data. POST/PUT/DELETE are never cached. Use dynamic = 'force-dynamic' to opt out.
Route handlers support ReadableStream for Server-Sent Events, chunked responses, and AI streaming — standard Web Streams API.
Route Handlers for external APIs, webhooks, and HTTP endpoints. Server Actions for UI-triggered mutations and form submissions.
Next.js provides built-in API endpoint capabilities so you can build your backend alongside your frontend without a separate server.
In the App Router, API endpoints are defined in route.ts (or route.js) files that export functions named after HTTP methods:
// app/api/users/route.ts
export async function GET(request: Request) {
const users = await db.users.findMany();
return Response.json(users);
}
export async function POST(request: Request) {
const body = await request.json();
const user = await db.users.create({ data: body });
return Response.json(user, { status: 201 });
}Supported methods: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS. Unsupported methods return 405 Method Not Allowed automatically.
Route handlers use the standard Web API Request and Response objects — not the Node.js-specific req/res from Pages Router. This makes them compatible with edge runtime and standard across platforms.
Dynamic segments work the same as pages: app/api/users/[id]/route.ts receives params as the second argument:
export async function GET(
request: Request,
{ params }: { params: Promise<{ id: string }> }
) {
const { id } = await params;
const user = await db.users.findById(id);
if (!user) return Response.json({ error: 'Not found' }, { status: 404 });
return Response.json(user);
}In Next.js 15+, params is a Promise that must be awaited.
GET route handlers are cached by default when they don't read the request (no request parameter usage, no dynamic functions). To opt out:
request object (reading headers, cookies, searchParams)const dynamic = 'force-dynamic'POST, PUT, PATCH, and DELETE handlers are never cached.
Route handlers support streaming with ReadableStream:
export async function GET() {
const stream = new ReadableStream({
async start(controller) {
controller.enqueue(new TextEncoder().encode('data: hello\n\n'));
controller.close();
}
});
return new Response(stream, { headers: { 'Content-Type': 'text/event-stream' } });
}This enables Server-Sent Events (SSE), chunked responses, and AI streaming patterns.
Server Actions ('use server' functions) handle mutations triggered from the UI — form submissions, button clicks, data updates. Route Handlers serve external API consumers, webhooks, and endpoints that need standard HTTP semantics (status codes, headers, content negotiation).
Use Server Actions for: form submissions, UI-triggered mutations, optimistic updates. Use Route Handlers for: external API consumers, webhooks, authentication callbacks, file downloads, SSE/streaming.
The Pages Router uses pages/api/ directory with Node.js-style handler functions: export default function handler(req, res) { res.status(200).json({ data }) }. These use NextApiRequest/NextApiResponse — Node.js-specific APIs not compatible with edge runtime. New projects should use App Router route handlers.
Route handlers are route.ts files exporting HTTP method functions (GET, POST, etc.) using standard Web Request/Response APIs. They run only on the server, support streaming, and can run at the edge. GET handlers are cached by default unless they read dynamic request data. Use Route Handlers for external APIs and webhooks; use Server Actions for UI-triggered mutations.
Fun Fact
Route Handlers replaced Pages Router API routes partly because the old API used Node.js-specific req/res objects that couldn't run at the edge. By adopting standard Web APIs (Request/Response), Next.js route handlers work identically in Node.js, edge runtime, and even Cloudflare Workers.