Nested Routes
Nested routes are built from folder depth: each folder under your apiDir adds one segment to the URL. You can mix static segments, dynamic segments, and route groups at any level.
Use nesting to model real structures, such as a user's posts.
Example
api/users/route.ts → /api/users
api/users/[id]/route.ts → /api/users/:id
api/users/[id]/posts/route.ts → /api/users/:id/posts
Each deeper folder extends the path. Static, dynamic, and grouped routes all nest the same way.
For each route type, see Static API Routes, Dynamic Routes, and Route Groups.
Types for this feature
Nesting does not change the types. Each dynamic segment still needs a params schema in schema.ts to be typed.
✅ Correct: one schema for all segments, wrapped with defineRoute:
import { defineRoute } from "burger-api";
import { GET as GetSchema } from "./schema";
export const GET = defineRoute(GetSchema, (ctx) => {
const { id, postId } = ctx.validated.params; // both typed
return Response.json({ id, postId });
});
// schema.ts
import { z } from "zod";
export const GET = {
params: z.object({
id: z.string(),
postId: z.string(),
}),
};
The manual generic, BurgerContext<typeof GetSchema>, is the same inference, written by hand.
❌ Wrong: a segment without a schema is unknown:
export const GET = defineRoute(GetSchema, (ctx) => {
ctx.validated.params.missing; // ❌ Property 'missing' does not exist
});
See the TypeScript overview.
Check your code: bun run typecheck.