Middleware@rhythmjs/middleware
API reference
Every export of the three subpaths: @rhythmjs/middleware/validate, /intercept, and /filter. There is no root barrel export.
@rhythmjs/middleware/validate#
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#
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#
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.