CORE CONCEPTS

Validation

Rasengan Server integrates @rasenganjs/validators directly — attach a schema to a route and validation middleware is injected automatically, ahead of your handler.

Per-route schema
import z from "zod"; routes(router: Router) { router.post("/users", this.create, { body: z.object({ name: z.string() }) }); }

If validation fails, the handler never runs — the request short-circuits with a 400 JSON error response by default.

Global Configuration

app.configureValidation()
import { zodAdapter } from '@rasenganjs/validators'; bootstrap((app) => { app.configureValidation({ adapter: zodAdapter, // this is already the default onError: (errors, ctx) => Response.json({ code: 'VALIDATION_ERROR', errors }, { status: 422 }), }); });

ServerApp defaults to zodAdapter and a 400 JSON error handler — you only need configureValidation() to change the adapter or customize the error response shape/status.

What Gets Validated

SchemaDefinition
interface SchemaDefinition { body?: ZodSchema; params?: ZodSchema; query?: ZodSchema; onError?: ( errors: ValidationError[], ctx: Context ) => Response | Promise<Response>; // per-route override }
All three at once
router.put('/users/:id', this.update, { params: z.object({ id: z.coerce.number() }), query: z.object({ notify: z.coerce.boolean().optional() }), body: z.object({ name: z.string().optional() }), });
  • params — validated data is merged into ctx.params in place.
  • query — validated data is available via ctx.get('validatedQuery') (not merged into ctx.query itself).
  • bodyctx.body is replaced with the validated/coerced data (and also stored under ctx.get('parsedBody')).

Per-route Error Override

Custom error handling for one route
router.post('/users', this.create, { body: CreateUserSchema, onError: (errors) => Response.json({ errors }, { status: 422 }), });

A schema's own onError takes priority over the global configureValidation({ onError }) handler for that one route.

Controller-level Schemas

Declaring schemas once on the controller and referencing them by method name avoids repeating the schema at every router.post(...) call — see Controllers & Routing for the full pattern, including why the schema is still passed as a trailing argument for TypeScript inference even though the runtime also matches it by method name.

Middleware
File Uploads