Route Config
Route-level configuration: validation, docs, and behavior.
Use this to declare how a single route accepts input (body, query, params, headers), how it is documented (OpenAPI), and which middlewares apply only to this route.
In most cases, you won’t need to define every field of the RouteConfig
in order to achieve solid validation and comprehensive documentation.
However, for completeness, let’s go through each option in detail and review what it provides.
Options
/**
* Request body validation & typing (Zod).
* If omitted, the body is not validated and is treated as `any`.
*
* @example z.object({ name: z.string(), age: z.number().int().optional() })
*/
body?: BODY;
/**
* Query string validation & typing (Zod).
* If omitted, the query is not validated and is treated as `any`.
*
* @example z.object({ page: z.coerce.number().min(1).default(1) })
*/
query?: QUERY;
/**
* Path params documentation.
* If omitted, params are inferred from `PATH` (e.g. "/users/:id") and documented automatically.
*
* @example { id: { description: "User identifier", example: "123" } }
*/
params?: PathParams<PATH>;
/**
* Request headers validation & typing (Zod).
* If omitted, headers are not validated.
* Tip: use lowercase header names to align with the Fetch `Headers` behavior.
*
* @example z.object({ 'x-request-id': z.string().uuid().optional() })
*/
headers?: HEADERS;
/**
* Route-specific middleware(s). Runs before the handler.
* Accepts a single middleware or an array.
*/
use?: Middleware | Middleware[];
/**
* Required request Content-Type. If set, non-matching requests may be rejected.
* Typical values: "application/json", "application/x-www-form-urlencoded", "multipart/form-data".
*/
type?: ContentType;
/**
* OpenAPI responses for this route.
* Use the `spec.response` helper to keep definitions DRY.
*
* @example spec.response(200, UserSchema, 'application/json')
*/
responses?: ResponseConfig[];
/**
* Exclude this route from the generated OpenAPI spec.
* @default false
*/
hide?: boolean;
/**
* OpenAPI tags for grouping.
* You can pre-declare tags with descriptions via the `openapi` helper to avoid duplication.
*
* @example ['Users']
* @example
* import { openapi } from '@crumbjs/core';
* openapi.addTag('Users', 'Operations related to user management');
* @default ['Uncategorized']
*/
tags?: string[];
/** OpenAPI: route description (supports Markdown). */
description?: string;
/** OpenAPI: short summary shown in UIs. */
summary?: string;
/**
* OpenAPI security requirement for this route.
* Make sure the corresponding security scheme exists in your OpenAPI components.
*/
authorization?: 'bearer' | 'basic';
/**
* Explicit operationId for OpenAPI.
* If omitted, it is inferred from the final resolved path (and method).
*/
operationId?: string;
/**
* **Use it with caution** the types and documentation will be infered any way,
* but the data will not be validated
*/
disableValidation?: boolean;
Example
import { z } from "zod";
import { App, spec } from "@crumbjs/core";
// Schemas
const createUserRequest = z.object({
name: z.string().min(1),
email: z.string().email(),
age: z.number().int().min(0).optional(),
});
const userSchema = z.object({
id: z.string().uuid(),
name: z.string(),
email: z.string().email(),
age: z.number().int().nullable(),
});
const headerSchema = z.object({
"x-request-id": z.string().uuid().optional(),
});
const app = new App();
// Full RouteConfig example with POST
app.post(
"/users",
async (ctx) => {
const { body, headers } = ctx; // or define handler like this async ({ body, headers }) => { ... }
// Simulate persistence
const newUser = {
id: crypto.randomUUID(),
name: body.name,
email: body.email,
age: body.age ?? null,
};
console.log("Request ID:", headers["x-request-id"]);
return newUser;
},
{
body: createUserRequest,
headers: headerSchema,
type: "application/json",
responses: [
spec.response(201, userSchema, "application/json"),
spec.invalid(userSchema), // spec.invalid(schema) documents automatically a 400 Bad Request Response based on your body request schema
],
use: [
async (c, next) => {
console.log("Before create user");
await next();
},
],
hide: false,
tags: ["Users"],
description: "Create a new user with name, email, and optional age.",
summary: "Create user",
authorization: "bearer",
operationId: "createUser",
disableValidation: false,
}
);