Route Handlers

Advanced route handling patterns

Type-Safe Handlers

Create fully type-safe route handlers:

import { route } from "@ademattos/bunbox"; interface User { id: string; name: string; email: string; } export const getUser = route.get().handle<User>(async (ctx) => { return { id: "1", name: "John Doe", email: "[email protected]", }; });

Request Body

Access and parse request bodies:

export const createUser = route.post().handle(async (ctx) => { const body = ctx.body; // Already parsed JSON return { received: body }; });

Response Helpers

Use built-in response helpers:

export const getUser = route.get().handle(async (ctx) => { // JSON response with ctx.json helper return ctx.json({ data: "hello" }); // Or return directly (auto-wrapped in JSON) return { data: "hello" }; // Custom status code return ctx.json({ created: true }, 201); });

Custom Status Codes

Return custom status codes:

export const createUser = route.post().handle(async (ctx) => { return ctx.json({ created: true }, 201); }); export const deleteUser = route.delete().handle(async (ctx) => { return ctx.json({ deleted: true }, 204); });

Error Responses

Return error responses:

import { route, error } from "@ademattos/bunbox"; export const getUser = route.get().handle(async (ctx) => { if (!ctx.query.id) { return error("ID is required", 400); } return { id: ctx.query.id }; });

Async Handlers

All handlers support async operations:

export const getData = route.get().handle(async (ctx) => { const data = await fetchFromDatabase(); const processed = await processData(data); return { result: processed }; });

Redirect

Redirect to another URL:

export const redirect = route.get().handle(async (ctx) => { return Response.redirect("/new-url", 302); });

File Downloads

Send files as downloads:

export const downloadReport = route.get().handle(async (ctx) => { const file = Bun.file("./data/report.pdf"); return new Response(file, { headers: { "Content-Type": "application/pdf", "Content-Disposition": "attachment; filename=report.pdf", }, }); });

Handling Multiple Methods

Handle multiple HTTP methods in one file:

import { route } from "@ademattos/bunbox"; import { z } from "zod"; const UserSchema = z.object({ name: z.string(), email: z.string().email(), }); export const listUsers = route.get().handle(async (ctx) => { return { users: await getUsers() }; }); export const createUser = route.post().body(UserSchema).handle(async (ctx) => { return { user: await createUser(ctx.body) }; }); export const updateUser = route.put().body(UserSchema).handle(async (ctx) => { return { user: await updateUser(ctx.params.id, ctx.body) }; }); export const deleteUser = route.delete().handle(async (ctx) => { await deleteUser(ctx.params.id); return { deleted: true }; });

Query Parameters

Validate query parameters:

const QuerySchema = z.object({ page: z.coerce.number().default(1), limit: z.coerce.number().default(20), search: z.string().optional(), }); export const listUsers = route .get() .query(QuerySchema) .handle(async (ctx) => { // ctx.query is typed as { page: number, limit: number, search?: string } return { users: [], page: ctx.query.page }; });

Path Parameters

Validate path parameters:

const ParamsSchema = z.object({ id: z.string().uuid(), }); export const getUser = route .get() .params(ParamsSchema) .handle(async (ctx) => { // ctx.params.id is validated as UUID return { user: await findUser(ctx.params.id) }; });

OpenAPI Metadata

Add documentation metadata to routes:

export const createUser = route .post() .meta({ summary: "Create a new user", description: "Creates a user with the provided details", tags: ["users"], responses: { 201: { description: "User created" }, 409: { description: "Email already exists" }, }, }) .body(UserSchema) .handle(async (ctx) => { return ctx.json({ user: ctx.body }, 201); });

See OpenAPI & Swagger for auto-generated API documentation.