Next.js API Route Builder

Generate a Next.js API route with method handling and error responses

Build a Next.js App Router route.ts with exported GET and POST handlers, request body parsing, a validation stub, try/catch error handling, and typed NextResponse JSON responses with correct status codes. It runs free in your browser on Gera Tools, with nothing uploaded.

Last updated Source: Gera Tools

Where do App Router route handlers live?

In the Next.js App Router, route handlers go in a file named route.ts inside an app directory folder. The folder path becomes the URL, so app/api/users/route.ts serves /api/users.

A production-shaped Next.js route handler

The App Router replaced pages/api with file-based route handlers: a route.ts file exports one async function per HTTP method. A solid handler parses input, validates it, does its work inside a try/catch, and returns typed JSON with the right status. This builder assembles that structure for the methods you choose.

How it works

The tool generates an app/api/<resource>/route.ts. It imports { NextRequest, NextResponse } from 'next/server'. For each selected method it exports an async function: GET returns a list or item with NextResponse.json(data, { status: 200 }); POST awaits request.json(), runs a validation stub, and returns 201 on success or 400 on bad input; PUT and DELETE follow the same parse-validate-respond pattern. Every handler is wrapped in try/catch, logging the error and returning NextResponse.json({ error }, { status: 500 }) so a thrown exception never leaks a stack trace to the client.

App Router vs Pages Router — key differences

In the old pages/api pattern, a single default export handled all methods and you branched on req.method. The App Router replaces that with one named export per method, which is cleaner and allows Next.js to tree-shake unused handlers. The request object is now the standard Web API Request (or NextRequest for extras like cookies), not a Node.js IncomingMessage, so you use await request.json() instead of req.body.

Reading dynamic segments

If your route is at app/api/users/[id]/route.ts, the second argument to each handler is a context object with the segment values:

export async function GET(
  request: NextRequest,
  { params }: { params: { id: string } }
) {
  const { id } = params;
  // fetch user by id
}

Real validation example with Zod

Replace the generated validation stub with a Zod schema for production-quality input checking:

import { NextRequest, NextResponse } from "next/server";
import { z } from "zod";

const CreateUserSchema = z.object({
  name: z.string().min(1),
  email: z.string().email(),
});

export async function POST(request: NextRequest) {
  try {
    const body = await request.json();
    const result = CreateUserSchema.safeParse(body);
    if (!result.success) {
      return NextResponse.json(
        { error: result.error.flatten() },
        { status: 400 }
      );
    }
    // result.data is typed and safe to use
    return NextResponse.json({ id: 1, ...result.data }, { status: 201 });
  } catch {
    return NextResponse.json({ error: "Invalid request" }, { status: 500 });
  }
}

Practical tips

  • Keep handlers thin — delegate business logic to a service module so the route file is only about HTTP parsing and response shaping.
  • Use 201 Created after a successful POST and return the created resource in the body so callers do not need a second GET.
  • For the Edge runtime, export export const runtime = 'edge' — but verify your code avoids Node-only APIs like fs first.
  • Never return a raw error stack trace to the client; catch all exceptions and return a clean { error: string } at 500.