Dynamic API Routes

Use [param] folders in API routes and read the value from the async context params.

Syntaxapp/api/users/[id]/route.ts

Route Handlers support the same dynamic segments as pages. A folder like [id] captures part of the URL, delivered through the handler's second argument. As with pages in Next 15, params is a Promise you must await.

Example

Example Β· typescript
// app/api/users/[id]/route.ts  ->  GET /api/users/42
import { NextResponse } from 'next/server';

export async function GET(
  request: Request,
  { params }: { params: Promise<{ id: string }> }
) {
  const { id } = await params;
  const user = await getUser(id);
  return NextResponse.json(user);
}

When to use it

  • A /api/users/[id] handler returns, updates, or deletes a specific user based on the id in the URL.
  • A file download endpoint at /api/files/[filename] reads the filename param to stream the correct asset.
  • A versioned API uses /api/[version]/resource to serve different response shapes for v1 and v2 clients.

More examples

Dynamic param in route handler

The params argument in a dynamic route handler gives typed access to the URL segment captured by [id].

Example Β· ts
// app/api/users/[id]/route.ts
import { NextRequest, NextResponse } from 'next/server';

export async function GET(
  _req: NextRequest,
  { params }: { params: Promise<{ id: string }> }
) {
  const { id } = await params;
  const user = await db.user.findUnique({ where: { id } });
  if (!user) return NextResponse.json({ error: 'Not found' }, { status: 404 });
  return NextResponse.json(user);
}

PUT update by ID

Exporting PUT alongside GET on the same dynamic route enables full CRUD with matching HTTP semantics.

Example Β· ts
// app/api/posts/[id]/route.ts
import { NextRequest, NextResponse } from 'next/server';

export async function PUT(
  req: NextRequest,
  { params }: { params: Promise<{ id: string }> }
) {
  const { id } = await params;
  const body = await req.json();
  const post = await db.post.update({ where: { id }, data: body });
  return NextResponse.json(post);
}

DELETE handler returning 204

Returning a 204 No Content response is the correct HTTP pattern for a successful delete with no body.

Example Β· ts
// app/api/items/[id]/route.ts
import { NextRequest, NextResponse } from 'next/server';

export async function DELETE(
  _req: NextRequest,
  { params }: { params: Promise<{ id: string }> }
) {
  const { id } = await params;
  await db.item.delete({ where: { id } });
  return new NextResponse(null, { status: 204 });
}

Discussion

  • Be the first to comment on this lesson.
Dynamic API Routes β€” Next.js | SoundsCode