File-Based Routing
BurgerAPI uses file-based routing: the folders and files under your apiDir (with apiPrefix applied) become the URL paths. There is no manual route registration. Each folder adds a segment to the URL, and a file named route.ts at a path defines the handlers for that path.
Convention files also accept .js and .mjs extensions, so a JavaScript project uses route.js, schema.js, and so on.
Example
api/products/route.ts → /api/products
api/users/[id]/route.ts → /api/users/:id
Inside route.ts, export a function for each HTTP method you want to support (GET, POST, PUT, DELETE, and so on).
Route types
BurgerAPI supports a few route shapes, all built from the same folder rules:
- Static routes: fixed paths. See Static API Routes.
- Dynamic routes: capture a segment with
[param]. See Dynamic Routes. - Wildcard routes: match the rest of the path with a
[...]folder. The folder is anonymous: a named folder such as[...slug]fails in dev and in builds. See Wildcard Routes. - Route groups: organize with
(folder)without changing the URL. See Route Groups.
These shapes nest freely by folder depth. See Nested Routes for how paths grow with folders.
Every route also gets automatic method handling: HEAD is derived from GET, and OPTIONS answers 204 with an Allow header, without extra handlers. See Automatic HEAD and Automatic OPTIONS.
Route collisions
API routes, pages, assets, /openapi.json and /docs share one URL space. If two of them resolve to the same path (for example an API route /docs and the docs UI), the app fails at startup with an error naming both sources. Rename one of them. Two dynamic page files at the same level ([a].tsx and [b].tsx) fail the same way.
For full API details and examples, see Static API Routes (API) and Static Pages (pages).
Types for this feature
Handlers take one argument: BurgerContext. When a route has a schema.ts, wrap the handler with defineRoute(schema, handler) and ctx.validated becomes typed from that schema automatically:
// api/products/route.ts
import { defineRoute } from "burger-api";
import { GET as GetSchema } from "./schema";
export const GET = defineRoute(GetSchema, (ctx) => {
ctx.validated.query; // typed from schema.ts
return Response.json({ ok: true });
});
The equivalent manual form, export async function GET(ctx: BurgerContext<typeof GetSchema>), still works if you'd rather write the generic by hand.
Each route type has its own type notes: Static, Dynamic, Wildcard, Nested. The full picture is in the TypeScript overview.
Check your code: bun run typecheck.