Useful Cheatsheetsusefulcheatsheets.com
Next.js API Routes
Chapter 10 · Page 70
Intermediate

Next.js API Routes

Build and handle API endpoints directly inside your Next.js application.

TL;DR

  1. 01Create API routes in app/api folder with route.ts files.
  2. 02Export handler functions for HTTP methods (GET, POST, etc).
  3. 03Access request data and return typed Response objects with status codes.

Tips

  1. 01Use "use server" functions in app/actions for simple mutations — API routes are for complex endpoints.

Warnings

  1. 01API routes are public by default — add authentication checks for sensitive endpoints.
Notes
Useful Cheatsheetsusefulcheatsheets.com
Next.js API Routes
Chapter 10 · Page 71
Intermediate

Next.js API Routes

(continued)

Basic API Route

  • Create API routes in app/api folder.
    // app/api/hello/route.ts
    export async function GET(request: Request) {
      return Response.json({ message: 'Hello' });
    }
  • Each route.ts file becomes an API endpoint.
  • /app/api/hello/route.ts becomes /api/hello.
  • Return a plain text response using the Response constructor.
    export async function GET() {
      return new Response("Hello, world!", {
        headers: { "Content-Type": "text/plain" }
      });
    }
  • Set response status and headers with the second argument.
    export async function POST() {
      return Response.json({ created: true }, { status: 201 });
    }
Notes
Useful Cheatsheetsusefulcheatsheets.com
Next.js API Routes
Chapter 10 · Page 72
Intermediate

Next.js API Routes

(continued)

HTTP Methods

  • Handle different HTTP methods with exports.
    // app/api/posts/route.ts
    export async function GET() {
      const posts = await fetchPosts();
      return Response.json(posts);
    }
    
    export async function POST(request: Request) {
      const body = await request.json();
      const post = await createPost(body);
      return Response.json(post, { status: 201 });
    }
  • Supports GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS.
  • Handle PUT requests to update an existing resource.
    export async function PUT(request: Request) {
      const body = await request.json();
      const updated = await updatePost(body.id, body);
      return Response.json(updated);
    }
  • Handle DELETE to remove a resource by ID.
    export async function DELETE(request: Request) {
      const { id } = await request.json();
      await deletePost(id);
      return Response.json({ deleted: true });
    }
  • Return 405 for unsupported methods.
    export async function PATCH() {
      return Response.json({ error: "Method not allowed" }, { status: 405 });
    }
Notes
Useful Cheatsheetsusefulcheatsheets.com
Next.js API Routes
Chapter 10 · Page 73
Intermediate

Next.js API Routes

(continued)

Request Handling

  • Read JSON body from requests.
    export async function POST(request: Request) {
      const body = await request.json();
      console.log(body);
      return Response.json({ success: true });
    }
  • Access query parameters and authorization headers.
    export async function GET(request: Request) {
      const { searchParams } = new URL(request.url);
      const id = searchParams.get('id');
      const auth = request.headers.get('authorization');
      return Response.json({ id, auth });
    }
  • Parse form data from multipart requests.
    export async function POST(request: Request) {
      const formData = await request.formData();
      const name = formData.get("name") as string;
      return Response.json({ name });
    }
  • Stream a large response body using ReadableStream.
    export async function GET() {
      const stream = new ReadableStream({
        start(controller) {
          controller.enqueue(new TextEncoder().encode("chunk 1"));
          controller.close();
        }
      });
      return new Response(stream);
    }
  • Validate request body shape before processing.
    const { name, email } = await request.json();
    if (!name || !email) {
      return Response.json({ error: "name and email are required" }, { status: 400 });
    }
Notes
Useful Cheatsheetsusefulcheatsheets.com
Next.js API Routes
Chapter 10 · Page 74
Intermediate

Next.js API Routes

(continued)

Cookies and Headers

  • Read cookies from incoming requests — cookies() is async in Next.js 15.
    import { cookies } from "next/headers";
    
    export async function GET() {
      const cookieStore = await cookies(); // must await in Next.js 15
      const token = cookieStore.get("token")?.value;
      return Response.json({ token });
    }
  • Set cookies in the response.
    import { cookies } from "next/headers";
    
    export async function POST() {
      const cookieStore = await cookies();
      cookieStore.set("session", "abc123", { httpOnly: true });
      return Response.json({ ok: true });
    }
  • Read request headers directly from the Request object.
    export async function GET(request: Request) {
      const auth = request.headers.get("authorization");
      const contentType = request.headers.get("content-type");
      return Response.json({ auth, contentType });
    }
  • Set custom response headers.
    export async function GET() {
      return new Response(JSON.stringify({ ok: true }), {
        headers: {
          "Content-Type": "application/json",
          "Cache-Control": "no-store"
        }
      });
    }
  • Use next/headers headers() to read request headers server-side (also async in Next.js 15).
    import { headers } from "next/headers";
    const headersList = await headers();
    const userAgent = headersList.get("user-agent");
Notes
Useful Cheatsheetsusefulcheatsheets.com
Next.js API Routes
Chapter 10 · Page 75
Intermediate

Next.js API Routes

(continued)

Error Handling and Status Codes

  • Return appropriate status codes.
    export async function GET(request: Request) {
      try {
        const data = await fetchData();
        return Response.json(data);
      } catch (error) {
        return Response.json(
          { error: 'Failed to fetch data' },
          { status: 500 }
        );
      }
    }
  • Use Response.json with status and headers.
    return Response.json(data, {
      status: 201,
      headers: { 'Content-Type': 'application/json' }
    });
  • Return 400 for invalid or missing request body fields.
    export async function POST(request: Request) {
      const body = await request.json();
      if (!body.name) {
        return Response.json({ error: "Name is required" }, { status: 400 });
      }
      return Response.json({ ok: true });
    }
  • Return 401 for unauthenticated requests.
    const token = request.headers.get("authorization");
    if (!token) {
      return Response.json({ error: "Unauthorized" }, { status: 401 });
    }
  • Log errors server-side before returning a generic message.
    } catch (error) {
      console.error("API error:", error);
      return Response.json({ error: "Internal server error" }, { status: 500 });
    }
Notes
Useful Cheatsheetsusefulcheatsheets.com
Next.js API Routes
Chapter 10 · Page 76
Intermediate

Next.js API Routes

(FAQ)

FAQ

Create a file at app/api/[route]/route.ts and export named async functions matching HTTP methods (GET, POST, PUT, DELETE). Each function receives a Request object and must return a Response, e.g. return Response.json({ data }) or new Response(body, { status: 201 }).

Use new URL(request.url).searchParams to access query parameters, and await request.json() to parse a JSON body. For form data, use await request.formData() instead.

Name your folder with brackets, e.g. app/api/users/[id]/route.ts, then access the param via the second argument: export async function GET(request, { params }) { const { id } = await params; }.

Use Server Actions ("use server") for form submissions and simple data mutations tied to UI components — they require less boilerplate. Use API routes when you need a public HTTP endpoint, webhook receiver, or need full control over headers, status codes, and response shape.

Pass a status option to the Response constructor or Response.json(): return Response.json({ error: 'Not found' }, { status: 404 }). For errors, always set an explicit status code — omitting it defaults to 200 even on failure.