Careeroza
class-validator Decorators
Adding Rules
src/products/dto/create-product.dto.ts
import { Type } from 'class-transformer';
import { IsIn, IsInt, IsNotEmpty, IsOptional, IsPositive, IsString, Length, Max, Min } from 'class-validator';
export const CATEGORIES = ['audio', 'mobiles', 'laptops', 'wearables', 'accessories'] as const;
export class CreateProductDto {
@IsString()
@Length(3, 120)
name: string;
@IsOptional()
@IsString()
@Length(0, 2000)
description?: string;
@Type(() => Number)
@IsPositive()
@Max(1_000_000)
price: number;
@IsInt()
@Min(0)
stock: number;
@IsNotEmpty()
@IsIn(CATEGORIES)
category: string;
}Common Decorators
| Decorator | Checks |
|---|---|
@IsString(), @IsInt(), @IsNumber(), @IsBoolean() | Type |
@IsEmail(), @IsUrl(), @IsUUID(), @IsMobilePhone('en-IN') | Format |
@Min(), @Max(), @IsPositive(), @Length() | Range and length |
@IsOptional() | Skip other rules if the value is missing |
@IsIn([...]), @IsEnum(Enum) | Allowed values |
@Matches(/regex/) | Pattern (PIN code, GSTIN) |
@ValidateNested() + @Type(() => Dto) | Nested objects and arrays |
@IsArray(), @ArrayMinSize(1) | Arrays |
How Decorator Validation Works
Example
class-validator stores rules per property when the class is defined, then checks an object against them. A mini version with legacy property decorators:
type Rule = { property: string; check: (v: unknown) => boolean; message: string };
const rules = new Map<Function, Rule[]>();
function addRule(check: (v: unknown) => boolean, message: (p: string) => string): PropertyDecorator {
return (target, key) => {
const list = rules.get(target.constructor) ?? [];
list.push({ property: String(key), check, message: message(String(key)) });
rules.set(target.constructor, list);
};
}
const IsString = () => addRule((v) => typeof v === 'string', (p) => p + ' must be a string');
const IsPositive = () => addRule((v) => typeof v === 'number' && v > 0, (p) => p + ' must be a positive number');
const Min = (n: number) => addRule((v) => typeof v === 'number' && v >= n, (p) => p + ' must not be less than ' + n);
class CreateProductDto {
@IsString() name!: string;
@IsPositive() price!: number;
@Min(0) stock!: number;
}
function validate(cls: Function, body: Record<string, unknown>): string[] {
return (rules.get(cls) ?? []).filter((r) => !r.check(body[r.property])).map((r) => r.message);
}
console.log(validate(CreateProductDto, { name: 'JBL Go 3', price: 2999, stock: 10 }));
console.log(validate(CreateProductDto, { name: 42, price: -5, stock: -1 }));Output
[] [ 'name must be a string', 'price must be a positive number', 'stock must not be less than 0' ]