Zod-backed, field- and method-level validation via native (TC39) decorators for
Node.js & TypeScript back-ends — the input-validation layer of the @imqueue
framework. Declare a validator next to a class field with @validate, seal the
class with @validatable, and validate method arguments with @validated.
Documentation: full guides, tutorial and API reference at imqueue.org. Commercial licensing & support for closed-source products at imqueue.com.
Using an AI assistant? Point it at imqueue.org/llms.txt for a machine-readable index of the docs, or see AGENTS.md.
Related packages:
- @imqueue/core - Fast JSON message queue over Redis for inter-service communication.
- @imqueue/rpc - RPC-like client/service implementation over @imqueue/core.
- @imqueue/pg-prisma - Prisma/Postgres toolkit for @imqueue services.
- Native TC39 decorators — no
experimentalDecorators, noreflect-metadata. Works under the standard TypeScript decorator emit. - Zod schemas — reuse any
zodschema as a field or argument validator. - Composable — a
@validatableclass can be used as a validator for a field or argument on another class, so nested input shapes validate recursively. - Method-argument validation —
@validated(...)checks positional arguments left-to-right; anull/undefinedentry skips that position. - TypeScript included!
Node.js ≥ 22.12. zod v4 is a runtime dependency.
npm i --save @imqueue/validationimport { z } from 'zod';
import {
validatable,
validate,
validated,
schemaOf,
} from '@imqueue/validation';
@validatable()
class Credentials {
@validate(z.string().min(3))
identifier!: string;
@validate(z.string().min(8))
secret!: string;
}
// A schema assembled from the field validators (or `null` if the class has none)
const schema = schemaOf(Credentials); // z.object({ identifier, secret })
class AuthService {
// Validate positional arguments before the method body runs.
// Pass a Zod schema, a `@validatable` class, or `null`/`undefined` to skip.
@validated(Credentials)
authenticate(creds: Credentials): string {
return creds.identifier;
}
}
new AuthService().authenticate({ identifier: 'ab', secret: 'short' });
// → throws ZodError (identifier too short, secret too short)TC39 decorator metadata (Symbol.metadata) is not populated by every build
tool (esbuild/tsx), so field validators are buffered as each class body
evaluates and sealed onto the class by the @validatable() class decorator.
Field decorators run before the class decorator and class bodies evaluate
sequentially, so the buffer reaches the right class — provided that class is
decorated. schemaOf(Class) then assembles the sealed field validators into a
z.object(...).
@validatable()is not optional. Nothing flushes the buffer when a class body merely ends, so a class using@validatewithout@validatable()hands its fields to the next class that is sealed. That class then rejects valid input over a property it never declared, while the class with the actual mistake validates nothing — the error surfaces on the innocent one.@validatedchecks arguments, it does not replace them. The method body receives exactly what the caller passed, so a transforming schema validates as expected and changes nothing downstream:z.coerce.number()accepts'42'and the parameter is still the string,.trim()hands over the untrimmed original,.default(...)fills nothing in, and object schemas leave undeclared properties in place. Parse in the body where the converted value is what you need.- Inherited fields are not inherited rules.
@validatable()seals the fields declared in that class body only, andschemaOfdoes not walk the prototype chain, so a subclass validates a parent's field only by re-declaring it.
Tests run on the native Node.js test runner (node:test) with node:assert and
no external test framework:
git clone git@github.com:imqueue/validation.git
cd validation
npm install
npm testTo produce a coverage report use:
npm run test-coverage # prints coverage summary to the console
npm run test-lcov # writes coverage/lcov.infoThis project is licensed under the GNU General Public License v3.0. See the LICENSE