Reza Golkhandani // Senior Software Engineer | Backend platforms
fastest-validator-nestjs: Type-Like Schemas That Validate at Compiled Speed
NestJS | Validation | Open source
fastest-validator-nestjs: Type-Like Schemas That Validate at Compiled Speed
The goal
Our API validated request bodies with the popular decorator library. It read well and it was, on the hottest endpoint, the single slowest step between socket and database, because it walked the schema with reflection on every request. A compiling validator was available, one that turns a schema into a straight-line function once, but it wanted plain schema objects. The goal was both: classes that read like types, and validation at the speed of a function call.
The approach
Give the compiling validator a decorator face. Collect rules in reflection metadata as the class is defined, compile once when the class decorator runs, and validate through a global pipe.
What I built
@ValidationSchema({ strict: true })
class CreateOrder {
@UUID() customerId: string;
@Enum({ values: ['pickup', 'delivery'] }) mode: 'pickup' | 'delivery';
@Numeric({ positive: true }) quantity: number;
@NestedObject() address: Address;
@ArrayOf({ items: LineItem }) items: LineItem[];
}
- Property decorators are thin factories over a rule template, so a new one is a single line.
- The class decorator marks strict mode, attaches any cross-field condition, compiles the schema, and stores the compiled function on the prototype. Once per class at load time, never per request.
- Nested classes unwrap into inner schemas at decoration time, so arrays of objects validate their items with no extra annotation.
- A global pipe validates any parameter whose class carries a schema; a global filter produces one consistent error body, stack hidden in production.
- Strict mode is the default: unknown properties are rejected, which catches stale clients and mass-assignment before the database layer.
The result
Validation on the hot endpoint dropped to a function call. The request class became the only schema, so nothing drifts. And the stable error shape turned out to matter more than the speed, because the frontend could finally rely on it. The package is on npm and has stayed small on purpose.
Takeaways
- Make the request class the schema. One source of truth per shape.
- Compute at boot what you can. Per-request work is a tax on every user.
- Strict by default at the API edge. Opting out is a visible decision per class.