0Pricing
Next.js 15 Fullstack (App Router + Server Actions) · Aula

Projeto de manipuladores de rotas RESTful com a API de requisições Web

Implemente manipuladores GET/POST/PATCH/DELETE usando os objetos nativos Request e Response e segmentos dinâmicos.

Projeto de manipuladores de rotas RESTful com a API de requisições Web é uma aula grátis de Next.js 15 Fullstack (App Router + Server Actions) no CoddyKit. Esta é a aula 1 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Next.js 15 Fullstack (App Router + Server Actions), e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Next.js 15 Fullstack (App Router + Server Actions) inclui 4 aulas no total.

Partes desta aula ainda não foram traduzidas e aparecem em inglês.

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.

Perguntas Frequentes

A aula “Projeto de manipuladores de rotas RESTful com a API de requisições Web” é grátis?

Sim — o texto completo de “Projeto de manipuladores de rotas RESTful com a API de requisições Web” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Next.js 15 Fullstack (App Router + Server Actions), atualize para CoddyKit PRO. O curso de Next.js 15 Fullstack (App Router + Server Actions) inclui 4 aulas no total.

O que vou aprender em “Projeto de manipuladores de rotas RESTful com a API de requisições Web”?

Implemente manipuladores GET/POST/PATCH/DELETE usando os objetos nativos Request e Response e segmentos dinâmicos. Você pratica Next.js 15 Fullstack (App Router + Server Actions) com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar Next.js 15 Fullstack (App Router + Server Actions)?

Nenhuma experiência prévia é necessária. Next.js 15 Fullstack (App Router + Server Actions) no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 1 de 4.

Quanto tempo leva a aula “Projeto de manipuladores de rotas RESTful com a API de requisições Web”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de Next.js 15 Fullstack (App Router + Server Actions)?

Sim. Cada aula de Next.js 15 Fullstack (App Router + Server Actions) inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Projeto de manipuladores de rotas RESTful com a API de requisições Web
  2. Diferenças entre o ambiente de execução Node e Edge
  3. Respostas em fluxo e ReadableStream em manipuladores
  4. Validação de requisições e respostas JSON tipadas com Zod
← Voltar para Next.js 15 Fullstack (App Router + Server Actions)