Technology · Next.js
Next.js API Routes Patterns
Build REST APIs with route handlers, middleware, CORS, and auth patterns.
TL;DR
- 01Create API routes in app/api folder for backend endpoints.
- 02Use request and response objects to handle HTTP methods.
- 03Set up CORS headers to allow safe cross-origin API requests.
Basic Route Handlers
- Create a GET endpoint in app/api folder.
// app/api/users/route.ts export async function GET(request: Request) { return Response.json({ users: [] }); } - Handle different HTTP methods in the same file.
export async function GET(request: Request) { return Response.json({ method: "GET" }); } export async function POST(request: Request) { const data = await request.json(); return Response.json({ created: data }); } - Route handlers receive a Request and return a Response.
- Use Response.json() for JSON responses.
- Each function name corresponds to an HTTP method.
Dynamic Routes
- Create dynamic endpoints with bracket syntax.
// app/api/users/[id]/route.ts export async function GET( request: Request, { params }: { params: { id: string } } ) { return Response.json({ userId: params.id }); } - Access URL parameters through the params object.
- Use the same pattern for POST, PUT, DELETE, etc.
export async function PUT( request: Request, { params }: { params: { id: string } } ) { const data = await request.json(); return Response.json({ updated: params.id }); } - Return 404 when the resource doesn't exist.
export async function GET( request: Request, { params }: { params: { id: string } } ) { const user = await getUser(params.id); if (!user) { return Response.json({ error: "Not found" }, { status: 404 }); } return Response.json(user); } - Use catch-all segments for deeply nested dynamic routes.
// app/api/docs/[...path]/route.ts export async function GET( _req: Request, { params }: { params: { path: string[] } } ) { return Response.json({ segments: params.path }); }
Request Body Parsing
- Parse JSON from the request body.
export async function POST(request: Request) { const data = await request.json(); console.log(data); // { name: "Alice" } return Response.json({ success: true }); } - Handle form data for multipart uploads.
export async function POST(request: Request) { const formData = await request.formData(); const file = formData.get("file"); return Response.json({ uploaded: true }); } - Parse query parameters from the URL.
export async function GET(request: Request) { const { searchParams } = new URL(request.url); const query = searchParams.get("q"); return Response.json({ query }); }
CORS and Headers
- Set CORS headers for cross-origin requests.
export async function GET(request: Request) { const response = Response.json({ data: "hello" }); response.headers.set("Access-Control-Allow-Origin", "*"); response.headers.set("Access-Control-Allow-Methods", "GET, POST"); return response; } - Use middleware for consistent header handling.
// middleware.ts export function middleware(request: Request) { const response = Response.next(); response.headers.set("Access-Control-Allow-Origin", "*"); return response; } - Return OPTIONS response for preflight requests.
export async function OPTIONS() { return new Response(null, { headers: { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE" } }); }
Common Patterns
- Database queries in API routes.
import { prisma } from "@/lib/prisma"; export async function GET() { const users = await prisma.user.findMany(); return Response.json(users); } - Error handling with status codes.
export async function POST(request: Request) { try { const data = await request.json(); if (!data.name) { return Response.json( { error: "Name required" }, { status: 400 } ); } return Response.json({ success: true }); } catch (error) { return Response.json( { error: "Invalid request" }, { status: 500 } ); } } - Authentication in API routes.
export async function GET(request: Request) { const token = request.headers.get("authorization"); if (!token) { return Response.json({ error: "Unauthorized" }, { status: 401 }); } return Response.json({ data: "protected" }); }
Tip: Keep API logic separate by using helper functions and middleware to avoid duplicating common concerns like auth and CORS across routes.
Warning: Never expose secrets or sensitive data in API responses — always validate input and use proper authentication before returning user data.