What are dtos and how does validation work in NestJS?
DTOs and Validation in NestJS
DTO (Data Transfer Object) is a class that defines the shape of data flowing into your application. Combined with class-validator and class-transformer, NestJS provides powerful, declarative request validation.
Setup
bash
npm install class-validator class-transformerEnable the global ValidationPipe in main.ts:
typescript
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe({
whitelist: true, // strip unknown properties
forbidNonWhitelisted: true, // throw if unknown props sent
transform: true, // auto-transform types (string → number)
}));
await app.listen(3000);
}Creating a DTO
typescript
// dto/create-user.dto.ts
import {
IsString, IsEmail, IsOptional, IsInt,
MinLength, MaxLength, Min, Max, IsEnum
} from 'class-validator';
export enum UserRole {
USER = 'user',
ADMIN = 'admin',
}
export class CreateUserDto {
@IsString()
@MinLength(2)
@MaxLength(50)
name: string;
@IsEmail()
email: string;
@IsOptional()
@IsInt()
@Min(18)
@Max(120)
age?: number;
@IsOptional()
@IsEnum(UserRole)
role?: UserRole = UserRole.USER;
}Using DTO in Controller
typescript
@Controller('users')
export class UsersController {
@Post()
create(@Body() createUserDto: CreateUserDto) {
// Validation happens automatically!
// If invalid → 400 Bad Request with details
return this.usersService.create(createUserDto);
}
}Validation Error Response
When validation fails, NestJS automatically returns:
json
{
"statusCode": 400,
"message": [
"email must be an email",
"name must be longer than or equal to 2 characters"
],
"error": "Bad Request"
}Update DTO with PartialType
typescript
// dto/update-user.dto.ts
import { PartialType } from '@nestjs/mapped-types';
import { CreateUserDto } from './create-user.dto';
// All fields from CreateUserDto become optional
export class UpdateUserDto extends PartialType(CreateUserDto) {}Nested DTOs
typescript
import { Type } from 'class-transformer';
import { ValidateNested, IsArray } from 'class-validator';
export class AddressDto {
@IsString() street: string;
@IsString() city: string;
}
export class CreateUserDto {
@IsString() name: string;
@ValidateNested()
@Type(() => AddressDto)
address: AddressDto;
@IsArray()
@ValidateNested({ each: true })
@Type(() => AddressDto)
previousAddresses: AddressDto[];
}Custom Validation Decorator
typescript
import { registerDecorator, ValidationOptions } from 'class-validator';
export function IsSlug(validationOptions?: ValidationOptions) {
return (object: object, propertyName: string) => {
registerDecorator({
name: 'isSlug',
target: object.constructor,
propertyName,
options: validationOptions,
validator: {
validate(value: unknown) {
return typeof value === 'string' && /^[a-z0-9-]+$/.test(value);
},
defaultMessage: () => '$property must be a valid slug',
},
});
};
}Summary
DTOs with class-validator decorators provide declarative, automatic request validation in NestJS. Enable ValidationPipe globally with whitelist: true and transform: true for best security. Use PartialType for update DTOs and ValidateNested for complex nested objects.
Short Answer
Interview readyPremium
A concise answer to help you respond confidently on this topic during an interview.