Route Hooks
Route hooks run only for a given route. Define them in the route's hooks.ts file:
// src/api/users/hooks.ts
import type { BurgerContext } from "burger-api";
export const beforeRoute = [
async (ctx: BurgerContext) => {
if (ctx.query.admin !== "true") {
return Response.json({ error: "Forbidden" }, { status: 403 });
}
},
];
The handler lives in route.ts:
// src/api/users/route.ts
import type { BurgerContext } from "burger-api";
export async function GET(ctx: BurgerContext) {
return Response.json({ users: [] });
}
A route's hooks.ts can export any of: transform, beforeRoute, afterRoute, mapResponse, onError. onRequest is app-level only: define it in src/hooks.ts or a plugin, never in a route's hooks.ts. It runs before a route is matched, so a route-level export is ignored with a warning (and a RouteHooks-typed object rejects it at compile time).
Route beforeRoute hooks run after global beforeRoute hooks and after validation, before the handler. Route transform entries run after global ones, before validation. There is no folder or group inheritance: hooks apply only to their own route directory.
Use route hooks for admin checks, loading route-specific data, or any logic that applies to a single endpoint. See Hook System.
When a route hook reads ctx.validated (the route has a schema.ts), wrap the hooks with defineHooks(schema, hooks) (the defineRoute counterpart for hooks files) to get it typed:
// src/api/users/hooks.ts
import { defineHooks } from "burger-api";
import { GET as GetSchema } from "./schema";
export const { beforeRoute } = defineHooks(GetSchema, {
beforeRoute: (ctx) => {
ctx.validated.query; // typed, same schema as route.ts
},
});
Hooks run for every method of the route, so a slot that only one method declares (like body) is typed possibly undefined. Read it with ctx.validated.body?.name.
See Type Safety.