SyntaxStudy
Sign Up
Next.js Route Handlers in the App Router
Next.js Beginner 1 min read

Route Handlers in the App Router

Route Handlers replace the pages/api directory from the Pages Router. They are defined in route.ts (or route.js) files inside the app/ directory and export named async functions corresponding to HTTP methods: GET, POST, PUT, PATCH, DELETE, HEAD, and OPTIONS. A Route Handler and a page.tsx cannot exist in the same route segment — they are mutually exclusive. Route Handlers receive a NextRequest object (an extension of the standard Web API Request) and return a NextResponse or a standard Response object. You can read request headers with request.headers, parse the body with request.json() or request.formData(), and read query parameters from request.nextUrl.searchParams. NextResponse provides a static json() method for returning JSON responses with the correct Content-Type header. Route Handlers support the same dynamic segment syntax as pages. A file at app/api/users/[id]/route.ts handles requests to /api/users/123. The dynamic segment value is passed as params in the second argument to each handler function. Route Handlers can also be used for webhooks, OAuth callbacks, and any other server-side HTTP endpoint that does not need to render a page.
Example
// app/api/posts/[id]/route.ts

import { NextRequest, NextResponse } from 'next/server';
import { prisma } from '@/lib/prisma';
import { getUser } from '@/lib/auth';

// GET /api/posts/:id
export async function GET(
  request: NextRequest,
  { params }: { params: { id: string } },
) {
  const post = await prisma.post.findUnique({
    where: { id: params.id },
  });

  if (!post) {
    return NextResponse.json({ error: 'Not found' }, { status: 404 });
  }

  return NextResponse.json(post);
}

// PATCH /api/posts/:id
export async function PATCH(
  request: NextRequest,
  { params }: { params: { id: string } },
) {
  const user = await getUser();
  if (!user) {
    return NextResponse.json({ error: 'Unauthorised' }, { status: 401 });
  }

  const body = await request.json();
  const { title, content } = body;

  const updated = await prisma.post.update({
    where: { id: params.id, authorId: user.id },
    data: { title, content, updatedAt: new Date() },
  });

  return NextResponse.json(updated);
}

// DELETE /api/posts/:id
export async function DELETE(
  _request: NextRequest,
  { params }: { params: { id: string } },
) {
  const user = await getUser();
  if (!user) {
    return NextResponse.json({ error: 'Unauthorised' }, { status: 401 });
  }

  await prisma.post.delete({ where: { id: params.id, authorId: user.id } });
  return new Response(null, { status: 204 });
}