Next.js cookbook · Beginner
Build a validated JSON API with Route Handlers
Expose a REST endpoint from a Next.js app for a mobile client, with auth and input validation.
Approach
Create app/api/<resource>/route.ts exporting GET/POST functions. Authenticate from the Authorization header or session, validate JSON with Zod, return proper status codes with Response.json(), and keep business logic in shared server modules (also used by Server Components/Actions).
Solution
// app/api/projects/route.ts
import { z } from "zod";
import { type NextRequest } from "next/server";
const CreateProject = z.object({ name: z.string().trim().min(1).max(100), color: z.string().regex(/^#[0-9a-f]{6}$/i).optional() });
export async function GET(request: NextRequest) {
const user = await authenticate(request);
if (!user) return Response.json({ title: "Unauthorized" }, { status: 401 });
const limit = Math.min(Number(request.nextUrl.searchParams.get("limit")) || 20, 100);
const projects = await projectsService.list(user.id, limit);
return Response.json({ items: projects });
}
export async function POST(request: NextRequest) {
const user = await authenticate(request);
if (!user) return Response.json({ title: "Unauthorized" }, { status: 401 });
const body = await request.json().catch(() => null);
const parsed = CreateProject.safeParse(body);
if (!parsed.success) {
return Response.json({ title: "Validation failed", errors: parsed.error.flatten().fieldErrors }, { status: 422 });
}
const project = await projectsService.create(user.id, parsed.data);
return Response.json(project, { status: 201, headers: { Location: `/api/projects/${project.id}` } });
}
// app/api/projects/[id]/route.ts — params are async in Next.js 15+
export async function DELETE(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
// ...
}
Pitfalls
- Calling your own Route Handlers from Server Components via HTTP (call the shared function directly).
- Forgetting CORS headers/OPTIONS for cross-origin clients.
- Unbounded
limitparameters.
Variations
- tRPC or oRPC for typed APIs consumed by your own clients.
- A separate backend service when the API is large or shared by many apps.
Further reading: Next.js: Route Handlers
Reference: https://nextjs.org/docs/app/getting-started/route-handlers
More Next.js recipes
- Add a Content Security Policy with nonces in Next.js
- Add dark mode to Next.js without a flash
- Add SEO metadata, Open Graph and sitemaps
- Avoid data-fetching waterfalls in Server Components
- Create encrypted session cookies in Next.js
- Handle errors and 404s in the App Router
- Handle forms with Server Actions and validation
- Keep secrets and server code out of the client bundle