rhythmjs

Search documentation

Search guides, the tutorial and every package.

On this page

@rhythmjs/middleware/validate#

TypeScript
function validate<TTarget extends ValidationTarget, TSchema extends StandardSchemaV1>(
  target: TTarget,
  schema: TSchema,
): Middleware<ValidationContext>;

Returns middleware that extracts the request part named by target and validates it with any Standard Schema v1 schema, sync or async. On success it calls next({ valid }) with the schema output stored at ctx.valid[target], preserving values from earlier validators. On failure it stops the chain, sets status 400 with content type application/json, and writes a ValidationFailure body. A body target whose JSON cannot be parsed fails with the single issue "Malformed JSON in request body".

ValidationTarget#
"body" | "query" | "param". Body reads ctx.request.clone().json(); query collects URL search params, turning repeated keys into arrays; param reads ctx.params (empty object when absent).
ValidationContext#
The context shape validate runs against: RhythmHttpContext, optional router params, and an optional valid record keyed by target.
Validated<Target, Schema>#
Context-extension type for a route handler's TExtra parameter. Resolves to { valid: { [target]: schema output } }.
ValidationIssue#
Serialized issue: { message: string; path?: readonly (string | number)[] }. Symbol path keys are dropped.
ValidationFailure#
The 400 response body: { success: false; target: ValidationTarget; issues: readonly ValidationIssue[] }.

@rhythmjs/middleware/intercept#

TypeScript
function intercept<TSchema extends StandardSchemaV1>(
  schema: TSchema,
): Middleware<RhythmHttpContext>;

Returns middleware that awaits next(), then transforms the outgoing response through the schema. It only acts when the response status is 2xx and the body is a string; other responses pass through untouched. The body is parsed as JSON, or handed to the schema as a raw string when parsing fails. A string schema output is written back as-is; any other output is serialized as JSON with content type application/json. Undeclared fields do not survive the schema, so the output is exactly what the schema declares.

InterceptIssue#
Serialized issue: { message: string; path?: readonly (string | number)[] }. Symbol path keys are dropped.
InterceptFailure#
The body written when the response does not match the schema: { success: false; issues: readonly InterceptIssue[] }, with status set to 500.

@rhythmjs/middleware/filter#

TypeScript
function filter(
  onError?: (error: unknown, ctx) => void | Promise<void>,
): Middleware<RhythmHttpContext>;

class HttpError extends Error {
  constructor(status: number, message: string, details?: unknown);
  readonly status: number;
  readonly details?: unknown;
}

filter returns middleware that wraps next() in a try/catch; register it first so it is the outermost layer. A caught HttpError becomes a JSON FilterFailure response with the error's status, message, and details (details is omitted when undefined). Any other thrown value becomes { "success": false, "status": 500, "message": "Internal Server Error" }, so the original error message never reaches the client. When onError is provided it replaces the default mapping entirely: it receives the error and the context, may be async, and may rethrow.

HttpError(status, message, details?)#
Error subclass with name "HttpError" and readonly status and details. Throw it anywhere downstream of filter to produce that status.
FilterFailure#
The error response body: { success: false; status: number; message: string; details?: unknown }.