Response Mutation
Handlers always return a standard Web Response. Response changes are expressed through ctx.set, and the framework applies them once at the end of the lifecycle, after mapResponse, via applySet.
Setting status and headers
export async function GET(ctx: BurgerContext) {
ctx.set = {
status: 201,
headers: { "x-total": "42" },
};
return Response.json({ created: true });
}
ctx.set.statusoverrides the response status only when defined, and never replaces an error status (400or higher). Errors stay errors.ctx.set.headersis merged over the response's existing headers; explicit values win.ctx.set.headersis always defined (created on first access), soctx.set.headers["x-id"] = valuenever throws.- An array value appends each entry. A scalar
set-cookieis appended, so a cookie set by the handler and one set throughctx.setboth survive.
mapResponse
For response shaping that needs the actual response, use mapResponse hooks. They run nearest-first (Route → Global → Plugin → Framework), after afterRoute:
export const mapResponse = [
(ctx: BurgerContext) => (res: Response) => {
res.headers.set("x-server", "burger");
return res;
},
];
A mapResponse hook returns a function that receives the current response and returns the transformed response, or returns a Response to replace it, or undefined to continue. CORS and logger hooks use this phase to decorate responses.
applySet
At the single exit point of the request flow, applySet merges ctx.set into the outgoing Response:
- If
ctx.setis empty (no status, no headers), the originalResponseis returned unchanged, with no extra memory used. - It runs uniformly on handler responses and on the auto-
HEADresponses derived fromGET. - It also runs for 404, 405, auto-
OPTIONS, errors rendered byonError, pages, assets,/docsand/openapi.json. Actx.setmutation from anonRequesthook applies to those responses too.
Why a single merge step
Collecting response changes in ctx.set and applying them in one place keeps response handling predictable: hooks and handlers all influence the response the same way, and the framework controls exactly when the Response is finalized. This also keeps the code that runs for every request light on memory.