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;
/**
* Whether to parse the response using Zod (applies only to successful responses).
*
* If a default or `2xx` response schema is defined, the Processor will validate
* and transform the result with Zod before returning it.
*
* Example use case: hide or transform fields before sending data back to the client.
*
* The result will not be parsed even on true if your handler return a Response instance or raw string or null. Only object kind
*
* @default false
*/
parseResult?: boolean;
Autocomplete Config
CrumbJS makes it fast to get a typed, validated, and documented API running. If you don’t set a piece of config, CrumbJS fills in the gaps for you — including OpenAPI and (where applicable) the generated client.
Authorization
Classic case: a route protected by middleware that expects a Bearer token, sets a user in the request, and returns it.
app.get('/me', ({ get }) => get('user'), {
use: authMiddleware,
authorization: 'bearer'
})
You didn’t explicitly say that the request needs an Authorization header, you didn’t validate it before your logic, and you didn’t document it in OpenAPI. No problem — the core does it for you:
What CrumbJS autocompletes:
- Adds a string header validation for authorization.
- Marks authorization as required in OpenAPI.
- Registers a standard error response for missing input.
Validation
It’s tedious to define a validation rule (on body, headers, params, or query) and also remember to add the corresponding error responses and docs. CrumbJS helps here too: if a rule exists and you don’t define a 400 Bad Request response, CrumbJS will add it automatically.
app.post('/user', ({ body }) => inviteUser(body), {
body: z.object({
name: z.string().min(3),
email: z.email()
}),
})
What CrumbJS autocompletes
- Registers a 400 Bad Request response (validation errors).
- Documents the validation schema in OpenAPI.
- Keeps your config minimal without losing correctness.
Error Responses
Conceptual example defining all route config options (never needed)
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,
parseResult: false,
}
);