Useful Cheatsheetsusefulcheatsheets.com
Next.js API Routes Patterns
Chapter 01 · Page 7
Beginner

Next.js API Routes Patterns

Build REST APIs with route handlers, middleware, CORS, and auth patterns.

TL;DR

  1. 01Create API routes in app/api folder for backend endpoints.
  2. 02Use request and response objects to handle HTTP methods.
  3. 03Set up CORS headers to allow safe cross-origin API requests.

Tips

  1. 01Keep API logic separate by using helper functions and middleware to avoid duplicating common concerns like auth and CORS across routes.

Warnings

  1. 01Never expose secrets or sensitive data in API responses — always validate input and use proper authentication before returning user data.
Notes
Useful Cheatsheetsusefulcheatsheets.com
Next.js API Routes Patterns
Chapter 01 · Page 8
Beginner

Next.js API Routes Patterns

(continued)

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.
Notes
Useful Cheatsheetsusefulcheatsheets.com
Next.js API Routes Patterns
Chapter 01 · Page 9
Beginner

Next.js API Routes Patterns

(continued)

Params and Catch-All Routes

  • Access dynamic route params by awaiting the params object in Next.js 15.
    // app/api/users/[id]/route.ts
    export async function GET(
      request: Request,
      { params }: { params: Promise<{ id: string }> }
    ) {
      const { id } = await params; // params is a Promise in Next.js 15
      return Response.json({ userId: id });
    }
  • Use the same await pattern for PUT, DELETE, and PATCH handlers.
    export async function PUT(
      request: Request,
      { params }: { params: Promise<{ id: string }> }
    ) {
      const { id } = await params;
      const data = await request.json();
      return Response.json({ updated: id });
    }
  • Return 404 when the resource doesn't exist.
    const { id } = await params;
    const user = await getUser(id);
    if (!user) {
      return Response.json({ error: "Not found" }, { status: 404 });
    }
    return Response.json(user);
  • Use catch-all [...path] segments for deeply nested API paths.
    // app/api/docs/[...path]/route.ts
    export async function GET(
      _req: Request,
      { params }: { params: Promise<{ path: string[] }> }
    ) {
      const { path } = await params;
      return Response.json({ segments: path });
    }
  • Use optional catch-all [[...path]] when the base segment should also match.
    app/api/search/[[...filters]]/route.ts
    # matches /api/search AND /api/search/active/recent
Notes
Useful Cheatsheetsusefulcheatsheets.com
Next.js API Routes Patterns
Chapter 01 · Page 10
Beginner

Next.js API Routes Patterns

(continued)

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 });
    }
  • Validate required fields before processing and return 400 for bad input.
    const { name, email } = await request.json();
    if (!name || !email) {
      return Response.json({ error: "name and email are required" }, { status: 400 });
    }
  • Read a plain text body with request.text() for webhooks or raw payloads.
    export async function POST(request: Request) {
      const raw = await request.text(); // e.g. Stripe webhook payload
      const sig = request.headers.get("stripe-signature");
      return Response.json({ received: true });
    }
Notes
Useful Cheatsheetsusefulcheatsheets.com
Next.js API Routes Patterns
Chapter 01 · Page 11
Beginner

Next.js API Routes Patterns

(continued)

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 with a 204 status.
    export async function OPTIONS() {
      return new Response(null, {
        status: 204,
        headers: {
          "Access-Control-Allow-Origin": "*",
          "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE",
          "Access-Control-Allow-Headers": "Content-Type, Authorization"
        }
      });
    }
  • Restrict CORS to specific origins instead of wildcard for production.
    const origin = request.headers.get("origin");
    const allowed = ["https://myapp.com", "https://staging.myapp.com"];
    const allowOrigin = allowed.includes(origin ?? "") ? origin : "";
    response.headers.set("Access-Control-Allow-Origin", allowOrigin ?? "");
  • Add Cache-Control and custom response headers to control caching and metadata.
    return new Response(JSON.stringify(data), {
      headers: {
        "Content-Type": "application/json",
        "Cache-Control": "no-store",
        "X-Request-Id": crypto.randomUUID()
      }
    });
Notes
Useful Cheatsheetsusefulcheatsheets.com
Next.js API Routes Patterns
Chapter 01 · Page 12
Beginner

Next.js API Routes Patterns

(continued)

Auth and Error 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" });
    }
  • Extract API keys from headers and compare against an environment variable.
    const apiKey = request.headers.get("x-api-key");
    if (apiKey !== process.env.API_SECRET) {
      return Response.json({ error: "Forbidden" }, { status: 403 });
    }
  • Log errors server-side before returning a generic 500 message to the client.
    } catch (err) {
      console.error("[api/users] error:", err);
      return Response.json({ error: "Internal server error" }, { status: 500 });
    }
Notes
Useful Cheatsheetsusefulcheatsheets.com
Next.js API Routes Patterns
Chapter 01 · Page 13
Beginner

Next.js API Routes Patterns

(FAQ)

FAQ

Export named functions matching each HTTP method from your route.ts file, such as export async function GET(request: Request) and export async function POST(request: Request). Requests with unhandled methods automatically return a 405 response.

In the App Router, params are passed as the second argument to the handler. In Next.js 15, params is a Promise — type it as { params: Promise<{ id: string }> } and await it before accessing properties: const { id } = await params. The route file goes at app/api/users/[id]/route.ts.

In App Router route handlers, the body is a ReadableStream — you must call await request.json() or await request.text() to consume it. Also ensure the client sends a Content-Type: application/json header when posting JSON.

Export a dedicated OPTIONS handler that returns the required CORS headers with a 204 status: return new Response(null, { status: 204, headers: { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET,POST', 'Access-Control-Allow-Headers': 'Content-Type' } }). You must also include these headers on your actual GET/POST responses.

Pages Router uses pages/api/ with Node.js-style req/res objects, while App Router uses app/api/ with the standard Web Request/Response API. App Router route handlers support edge runtime and streaming natively, making them the better choice for new Next.js 13+ projects.