Dynamic Segments in Route Handlers

params in Handlers

app/api/products/[id]/route.ts — the second argument holds params, a Promise like in pages:

import { NextRequest } from 'next/server';
import { prisma } from '@/lib/prisma';

type Ctx = { params: Promise<{ id: string }> };

export async function GET(_req: NextRequest, { params }: Ctx) {
  const { id } = await params;
  const product = await prisma.product.findUnique({ where: { id: Number(id) } });
  if (!product) return Response.json({ error: 'Product ' + id + ' not found' }, { status: 404 });
  return Response.json(product);
}

export async function PATCH(req: NextRequest, { params }: Ctx) {
  const { id } = await params;
  const data = (await req.json()) as { price?: number; stock?: number };
  const product = await prisma.product.update({ where: { id: Number(id) }, data });
  return Response.json(product);
}

export async function DELETE(_req: NextRequest, { params }: Ctx) {
  const { id } = await params;
  await prisma.product.delete({ where: { id: Number(id) } });
  return new Response(null, { status: 204 });
}

Tip: Next.js also generates a RouteContext<'/api/products/[id]'> helper type for the second argument.

Requests

Example

curl localhost:3000/api/products/1
curl -X PATCH localhost:3000/api/products/1 -H "Content-Type: application/json" -d '{"stock":80}'
curl -i -X DELETE localhost:3000/api/products/1
curl -i localhost:3000/api/products/999

Example output

{"id":1,"name":"Redmi Note 14","price":17999,"stock":120}
{"id":1,"name":"Redmi Note 14","price":17999,"stock":80}
HTTP/1.1 204 No Content

HTTP/1.1 404 Not Found
{"error":"Product 999 not found"}