Next.js 15 Fullstack (App Router + Server Actions) · Lektion

RESTful-Route-Handler mit der Web Request API entwerfen

Implementieren Sie GET/POST/PATCH/DELETE-Handler mit den nativen Request- und Response-Objekten sowie dynamischen Segmenten.

Lektion 1 von 413 Schritte

RESTful-Route-Handler mit der Web Request API entwerfen ist eine kostenlose Next.js 15 Fullstack (App Router + Server Actions)-Lektion auf CoddyKit. Dies ist Lektion 1 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des Next.js 15 Fullstack (App Router + Server Actions)-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der Next.js 15 Fullstack (App Router + Server Actions)-Kurs umfasst insgesamt 4 Lektionen.

Teile dieser Lektion wurden noch nicht übersetzt und werden auf Englisch angezeigt.

What Route Handlers Are

In the Next.js 15 App Router, a Route Handler is a file named route.ts inside the app directory. It lets you build a REST-style API endpoint without a separate Express server.

  • You export an async function named after the HTTP method: GET, POST, PATCH, DELETE, PUT, HEAD, OPTIONS.
  • Each function receives a standard Web Request and returns a standard Web Response.
  • The URL is derived from the folder path: app/api/users/route.ts serves /api/users.

Because these are built on the platform's native Fetch API, the same mental model works on Node and Edge runtimes.

// app/api/users/route.ts
export async function GET(request: Request): Promise<Response> {
  return Response.json({ users: ["Ada", "Linus"] });
}

export async function POST(request: Request): Promise<Response> {
  const body = await request.json();
  return Response.json({ created: body }, { status: 201 });
}

Returning Responses

A handler must return a Response. Next.js gives you the native object plus a convenience helper.

  • Response.json(data, init) serializes data and sets Content-Type: application/json automatically.
  • Use the second init argument to set status and custom headers.
  • For plain text or other payloads, construct new Response(body, init) directly.

Picking the right status code is part of RESTful design: 200 for reads, 201 for creates, 204 for deletes with no body.

// Three idiomatic ways to respond
Response.json({ ok: true });                       // 200 + JSON
Response.json({ id: 1 }, { status: 201 });          // 201 Created
new Response(null, { status: 204 });                // 204 No Content
new Response("pong", {
  status: 200,
  headers: { "Content-Type": "text/plain" },
});

Reading the Request Body

The incoming Request is the same object you know from fetch on the client. Its body is a stream you consume once.

  • await request.json() parses a JSON payload.
  • await request.text() reads raw text.
  • await request.formData() reads multipart or URL-encoded form submissions.

You can only read the body once. If JSON parsing can fail (malformed input), wrap it in try/catch and return 400 Bad Request.

// app/api/posts/route.ts
export async function POST(request: Request) {
  let body: { title?: string };
  try {
    body = await request.json();
  } catch {
    return Response.json({ error: "Invalid JSON" }, { status: 400 });
  }
  if (!body.title) {
    return Response.json({ error: "title is required" }, { status: 422 });
  }
  return Response.json({ id: 1, title: body.title }, { status: 201 });
}

Reading Query Parameters

For GET requests, filters and pagination usually arrive as query-string parameters. Parse them from the request URL.

  • new URL(request.url) gives you a URL object.
  • Its searchParams is a URLSearchParams instance with get, getAll, and has.
  • Convert numeric params explicitly — every value comes in as a string.

Next.js also exposes nextUrl via NextRequest, but the native URL approach keeps your handler runtime-agnostic.

// app/api/products/route.ts  ->  /api/products?page=2&q=phone
export async function GET(request: Request) {
  const { searchParams } = new URL(request.url);
  const page = Number(searchParams.get("page") ?? "1");
  const q = searchParams.get("q") ?? "";
  return Response.json({ page, q });
}

Dynamic Segments and Async Params

To handle a single resource by id, create a dynamic folder like app/api/users/[id]/route.ts. The segment is passed as the second argument.

Important Next.js 15 change: the params object is now a Promise. You must await it before reading values.

  • Type the context as { params: Promise<{ id: string }> }.
  • const { id } = await params; unwraps the segment.
  • Segment values are always strings, so parse numbers yourself.
// app/api/users/[id]/route.ts
export async function GET(
  request: Request,
  { params }: { params: Promise<{ id: string }> }
) {
  const { id } = await params;
  const user = { id, name: "Ada" };
  return Response.json(user);
}

A Full GET-by-id with 404

RESTful reads should return the resource on success and a proper 404 Not Found when it does not exist. Never return 200 with an empty body for a missing record.

  • Look up the resource using the awaited id.
  • If nothing is found, return Response.json({ error }, { status: 404 }).
  • Otherwise return the resource with the default 200.

This handler is the canonical shape for /api/<resource>/[id].

// app/api/users/[id]/route.ts
const DB = new Map([["1", { id: "1", name: "Ada" }]]);

export async function GET(
  request: Request,
  { params }: { params: Promise<{ id: string }> }
) {
  const { id } = await params;
  const user = DB.get(id);
  if (!user) {
    return Response.json({ error: "User not found" }, { status: 404 });
  }
  return Response.json(user);
}

PATCH for Partial Updates

PATCH updates part of a resource, while PUT replaces it entirely. For most CRUD APIs you want PATCH: the client sends only the fields that change.

  • Read the dynamic id from the awaited params.
  • Parse the JSON body for the changed fields.
  • Merge the changes onto the existing record and return the updated resource with 200.

Return 404 if the target does not exist, and validate before merging.

// app/api/users/[id]/route.ts
export async function PATCH(
  request: Request,
  { params }: { params: Promise<{ id: string }> }
) {
  const { id } = await params;
  const existing = DB.get(id);
  if (!existing) {
    return Response.json({ error: "Not found" }, { status: 404 });
  }
  const changes = await request.json();
  const updated = { ...existing, ...changes, id };
  DB.set(id, updated);
  return Response.json(updated);
}

DELETE and 204 No Content

A successful DELETE typically returns 204 No Content with an empty body, signalling the resource is gone and there is nothing to send back.

  • Confirm the resource exists; if not, return 404.
  • Remove it from your store.
  • Return new Response(null, { status: 204 }) — do not call Response.json, since a 204 must have no body.

Some teams prefer 200 with the deleted object; both are valid, but be consistent across your API.

// app/api/users/[id]/route.ts
export async function DELETE(
  request: Request,
  { params }: { params: Promise<{ id: string }> }
) {
  const { id } = await params;
  if (!DB.has(id)) {
    return Response.json({ error: "Not found" }, { status: 404 });
  }
  DB.delete(id);
  return new Response(null, { status: 204 });
}

Reading and Setting Headers

Headers carry auth tokens, content negotiation, and caching hints. The native Request.headers and Response init both use the standard Headers API.

  • request.headers.get("authorization") reads an incoming header (case-insensitive).
  • Set response headers via the init.headers object or a Headers instance.
  • Common ones: Cache-Control, Location (for created resources), and WWW-Authenticate.

Returning 401 Unauthorized early keeps protected handlers clean.

// app/api/secret/route.ts
export async function GET(request: Request) {
  const auth = request.headers.get("authorization");
  if (auth !== "Bearer secret-token") {
    return Response.json({ error: "Unauthorized" }, { status: 401 });
  }
  return Response.json(
    { data: "top secret" },
    { headers: { "Cache-Control": "no-store" } }
  );
}

Caching and the Runtime

In Next.js 15, GET Route Handlers are not cached by default (this changed from Next.js 14). You opt into static caching explicitly.

  • Force caching with export const dynamic = 'force-static'.
  • Set a revalidation window with export const revalidate = 60 (seconds).
  • Reading the request body, headers, or cookies makes a handler dynamic automatically.

Choose the runtime with export const runtime = 'edge' for low-latency global execution, or the default 'nodejs' when you need Node APIs.

// app/api/quote/route.ts
export const runtime = "edge";
export const revalidate = 60; // re-generate at most once per minute

export async function GET() {
  return Response.json({ quote: "Stay curious", at: Date.now() });
}

A Pure Request Router You Can Run

Route Handlers are thin wrappers over Web Request/Response. To prove the model is just standard JavaScript, here is a tiny self-contained router that dispatches by method and parses an id from the path — no framework required.

  • It builds a real Request, inspects method and url, and returns a Response.
  • The same logic you would put inside GET/POST lives here.
  • This runs in any modern runtime with the Fetch API available.
async function handle(req: Request): Promise<Response> {
  const { pathname } = new URL(req.url);
  const id = pathname.split("/").pop();
  if (req.method === "GET") {
    return Response.json({ id, name: "Ada" });
  }
  if (req.method === "DELETE") {
    return new Response(null, { status: 204 });
  }
  return Response.json({ error: "Method Not Allowed" }, { status: 405 });
}

async function main() {
  const get = await handle(new Request("http://x/api/users/1"));
  console.log(get.status, await get.json());
  const del = await handle(
    new Request("http://x/api/users/1", { method: "DELETE" })
  );
  console.log(del.status); // 204
}
main();

Quick Check

You are writing app/api/users/[id]/route.ts in Next.js 15. How do you correctly read the id segment inside the GET handler?

Recap

You now know how to design RESTful Route Handlers on the native Web Request/Response API in Next.js 15.

  • Export method-named async functions (GET, POST, PATCH, DELETE) from route.ts.
  • Read input with request.json(), request.formData(), and new URL(request.url).searchParams.
  • Access dynamic segments via const { id } = await params — params is a Promise in v15.
  • Respond with Response.json(data, { status }); use 201 for creates, 404 for missing resources, and 204 (empty body) for deletes.
  • Remember GET is uncached by default; opt in with dynamic/revalidate, and pick runtime as needed.

These patterns give you clean, predictable, framework-agnostic API endpoints.

Kostenlos starten

Lerne TypeScript mit einem KI-Tutor — kostenlos

Schreibe und führe echten Code in deinem Browser aus, bekomme sofortige Hilfe von einem 24/7 KI-Tutor und setze dein Lernen im Web oder in der App fort.

Kurse
22
Lektionen
88

Häufig gestellte Fragen

Ist die Lektion „RESTful-Route-Handler mit der Web Request API entwerfen“ kostenlos?

Ja — der vollständige Text von „RESTful-Route-Handler mit der Web Request API entwerfen“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des Next.js 15 Fullstack (App Router + Server Actions)-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der Next.js 15 Fullstack (App Router + Server Actions)-Kurs umfasst insgesamt 4 Lektionen.

Was lerne ich in „RESTful-Route-Handler mit der Web Request API entwerfen“?

Implementieren Sie GET/POST/PATCH/DELETE-Handler mit den nativen Request- und Response-Objekten sowie dynamischen Segmenten. Du übst Next.js 15 Fullstack (App Router + Server Actions) mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.

Brauche ich Erfahrung, um Next.js 15 Fullstack (App Router + Server Actions) zu starten?

Keine Vorkenntnisse erforderlich. Next.js 15 Fullstack (App Router + Server Actions) auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 1 von 4.

Wie lange dauert die Lektion „RESTful-Route-Handler mit der Web Request API entwerfen“?

Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.

Kann ich in dieser Next.js 15 Fullstack (App Router + Server Actions)-Lektion Code schreiben und ausführen?

Ja. Jede Next.js 15 Fullstack (App Router + Server Actions)-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.

Alle Lektionen in diesem Kurs

  1. RESTful-Route-Handler mit der Web Request API entwerfen
  2. Abwägungen zwischen Node Runtime und Edge Runtime
  3. Streaming-Antworten und ReadableStream in Handlern
  4. Request-Validierung und typisierte JSON-Antworten mit Zod
← Zurück zu Next.js 15 Fullstack (App Router + Server Actions)