Как настроить валидацию с class-validator в NestJS?
Настройка валидации с class-validator в NestJS
Установка зависимостей
Для работы валидации необходимо установить два пакета:
npm install class-validator class-transformer
class-validator предоставляет декораторы для описания правил валидации, а class-transformer — преобразует plain-объекты запроса в экземпляры DTO-классов, без чего декораторы не работают.
Подключение ValidationPipe
Globalный пайп подключается в main.ts через app.useGlobalPipes():
// main.ts
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(
new ValidationPipe({
whitelist: true, // удаляет поля, не описанные в DTO
forbidNonWhitelisted: true, // выбрасывает ошибку при лишних полях
transform: true, // преобразует входные данные к типам DTO
}),
);
Опция transform: true критична — без неё class-transformer не создаёт экземпляр класса и декораторы class-validator не срабатывают.
Создание DTO с декораторами
// create-user.dto.ts
import { IsString, IsEmail, IsInt, Min, Max, IsOptional } from 'class-validator';
export class CreateUserDto {
@IsString()
name: string;
@IsEmail()
email: string;
@IsInt()
@Min(18)
@Max(120)
age: number;
@IsOptional()
@IsString()
bio?: string;
}
Использование DTO в контроллере
@Post('users')
createUser(@Body() dto: CreateUserDto) {
// если валидация не прошла — NestJS автоматически вернёт 400 Bad Request
return this.usersService.create(dto);
}
Вложенная валидация
Для объектов внутри DTO нужен дополнительный декоратор @ValidateNested и @Type:
import { ValidateNested } from 'class-validator';
import { Type } from 'class-transformer';
export class CreateOrderDto {
@ValidateNested()
@Type(() => AddressDto) // указываем класс для трансформации
address: AddressDto;
}
Кастомные сообщения об ошибках
@IsString({ message: 'Имя должно быть строкой' })
@MinLength(2, { message: 'Имя не может быть короче $constraint1 символов' })
name: string;
Альтернатива: pipe на уровне маршрута
Если глобальный пайп не подходит, его можно применить точечно:
@Post()
createUser(
@Body(new ValidationPipe()) dto: CreateUserDto
) { ... }
Частые проблемы
- Без
transform: trueвалидация не работает, потому что NestJS передаёт plain object, а не экземпляр класса. - Без
@Type(() => NestedDto)вложенные объекты не трансформируются и@ValidateNestedигнорируется.
Что хочет услышать интервьюер
Знание необходимых пакетов: class-validator и class-transformer — оба обязательны
Понимание роли ValidationPipe и важности опции transform: true
Умение создавать DTO-классы с декораторами валидации
Знание whitelist и forbidNonWhitelisted для защиты от лишних полей
Понимание как обрабатывать вложенные объекты через @ValidateNested и @Type
Пример: main.ts — подключение глобального ValidationPipe
import { NestFactory } from '@nestjs/core';
import { ValidationPipe } from '@nestjs/common';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(
new ValidationPipe({
whitelist: true, // удалить поля вне DTO
forbidNonWhitelisted: true, // ошибка при лишних полях
transform: true, // преобразовать plain-объект в экземпляр DTO
}),
);
await app.listen(3000);
}
bootstrap();
Пример: create-user.dto.ts — DTO с декораторами валидации
import {
IsString,
IsEmail,
IsInt,
Min,
Max,
IsOptional,
MinLength,
} from 'class-validator';
export class CreateUserDto {
@IsString()
@MinLength(2, { message: 'Имя слишком короткое' })
name: string;
@IsEmail({}, { message: 'Некорректный email' })
email: string;
@IsInt()
@Min(18)
@Max(120)
age: number;
@IsOptional()
@IsString()
bio?: string;
}
Пример: Вложенная валидация с @ValidateNested
import { ValidateNested, IsString } from 'class-validator';
import { Type } from 'class-transformer';
class AddressDto {
@IsString()
city: string;
@IsString()
street: string;
}
export class CreateOrderDto {
@ValidateNested()
@Type(() => AddressDto) // обязательно для трансформации
address: AddressDto;
}
Типичные ошибки
Забывают установить class-transformer или не указывают transform: true — декораторы молча не срабатывают
Подключают ValidationPipe только глобально через useGlobalPipes, но используют модульный подход — pipe, добавленный вне модуля, не подхватывает провайдеры
Не используют @ValidateNested + @Type для вложенных DTO — валидация вложенных объектов не выполняется
Используют интерфейсы вместо классов для DTO — декораторы работают только с классами
Включают whitelist: true, но не понимают, что это изменяет тело запроса (удаляет лишние поля)


