@zodec/nest
v0.0.7
Published
NestJS adapter for @zodec/core - Zod-powered validation with decorators. Drop-in replacement for class-validator with ValidationPipe.
Downloads
608
Maintainers
Readme
@zodec/nest
NestJS adapter for @zodec/core - Bring Zod schema validation with decorators to your NestJS applications.
zodec = zod[functional-schema] ++ class-validator[clean-declarative-style]
Overview
@zodec/nest provides seamless integration between @zodec/core and NestJS, allowing you to use Zod-powered validation with decorator syntax just like NestJS's native ValidationPipe works with class-validator.
Key Feature: ZodecValidationPipe - A drop-in replacement for NestJS's ValidationPipe that automatically validates request payloads against schemas defined using @zatt and @zclass decorators from @zodec/core.
Installation
npm install @zodec/nest @zodec/core zod reflect-metadata @nestjs/commonQuick Start
1. Define Your DTO/model with @zodec/core decorators
import "reflect-metadata";
import { z } from "zod";
import { zatt } from "@zodec/core";
export class CreatePizzaDto {
@zatt(z.string().min(1))
name: string;
@zatt(z.number().positive())
price: number;
@zatt(z.array(z.string()))
toppings: string[];
@zatt(z.boolean().optional())
isVeg?: boolean;
}2. Use ZodecValidationPipe in Your Controller
import { Controller, Post, Body, UsePipes } from '@nestjs/common';
import { ZodecValidationPipe } from '@zodec/nest';
import { CreatePizzaDto } from './dto/create-pizza.dto';
@Controller('pizzas')
export class PizzaController {
@Post()
@UsePipes(new ZodecValidationPipe())
create(@Body() createPizzaDto: CreatePizzaDto) {
// createPizzaDto is validated automatically
return { success: true, data: createPizzaDto };
}
}Usage
Global Validation Pipe
Apply ZodecValidationPipe globally to validate all incoming requests:
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ZodecValidationPipe } from '@zodec/nest';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ZodecValidationPipe({
transform: true, // Transform payloads to class instances
}));
await app.listen(3000);
}
bootstrap();Controller-Level Validation
Apply validation to specific controllers:
@Controller('users')
@UsePipes(new ZodecValidationPipe())
export class UserController {
// All routes in this controller use ZodecValidationPipe
}Route-Level Validation
Apply validation to individual routes:
@Post('register')
@UsePipes(new ZodecValidationPipe({ transform: true }))
async register(@Body() dto: RegisterDto) {
return this.authService.register(dto);
}Validation Options
ZodecValidationPipe extends NestJS's ValidationPipe and accepts all standard ValidationPipeOptions:
new ZodecValidationPipe({
transform: true, // Transform to class instance
whitelist: true, // Strip unknown properties
forbidNonWhitelisted: true, // Throw on unknown properties
})Complete Example
// create-order.dto.ts
import { z } from "zod";
import { zatt, zclass } from "@zodec/core";
export class OrderItemDto {
@zatt(z.string().uuid())
productId: string;
@zatt(z.number().int().positive())
quantity: number;
}
export class CreateOrderDto {
@zatt(z.string().email())
customerEmail: string;
@zatt(z.array(z.object({
productId: z.string().uuid(),
quantity: z.number().int().positive()
})))
items: OrderItemDto[];
@zatt(z.enum(['standard', 'express', 'overnight']))
shippingMethod: string;
}
// orders.controller.ts
import { Controller, Post, Body } from '@nestjs/common';
import { ZodecValidationPipe } from '@zodec/nest';
import { CreateOrderDto } from './dto/create-order.dto';
@Controller('orders')
export class OrdersController {
@Post()
async createOrder(
@Body(new ZodecValidationPipe({ transform: true }))
orderDto: CreateOrderDto
) {
// Validated and transformed orderDto
return { orderId: '123', ...orderDto };
}
}Error Response Format
When validation fails, ZodecValidationPipe returns a structured error response:
{
"success": false,
"message": "Validation failed",
"errors": {
"name": ["Required"],
"price": ["Number must be greater than 0"],
"email": ["Invalid email"]
}
}Using @zclass Decorator
For pre-defined schemas, use @zclass:
import { z } from "zod";
import { zclass } from "@zodec/core";
const userSchema = z.object({
username: z.string().min(3).max(20),
email: z.string().email(),
age: z.number().int().min(18)
});
@zclass(userSchema)
export class CreateUserDto {}Comparison with class-validator
| Feature | class-validator | @zodec/nest | |---------|----------------|-------------| | Validation Library | class-validator | Zod | | Decorator Style | ✅ Yes | ✅ Yes | | Schema Definition | Decorators only | Decorators + Zod schemas | | Type Inference | Limited | Full TypeScript + Zod | | Runtime Validation | ✅ | ✅ | | Composability | Moderate | High (Zod's power) | | Complex Validations | Custom validators | Zod's rich API |
Benefits
- NestJS Native Feel: Works exactly like NestJS's
ValidationPipewithclass-validator - Zod Power: Access to Zod's rich validation ecosystem and composability
- Type-Safe: Full TypeScript type inference from Zod schemas
- Declarative: Define validation rules inline with your DTO classes
- Drop-in Replacement: Easy migration from
class-validator - Automatic Validation: Validates all request payloads automatically
Requirements
reflect-metadatamust be imported once in your app entry point (typicallymain.ts)- TypeScript with
experimentalDecoratorsandemitDecoratorMetadataenabled intsconfig.json
License
MIT
Related Packages
- @zodec/core - Core decorator-based Zod schema generation
- zod - TypeScript-first schema validation
