NestJS предоставляет встроенный механизм валидации входящих данных
через ValidationPipe, который тесно интегрирован с
class-validator и class-transformer. Этот механизм является ключевым
элементом обработки DTO-слоя и обеспечивает декларативную проверку
входных структур без необходимости ручной валидации в контроллерах.
ValidationPipe функционирует на уровне пайпов NestJS и
применяется к входным данным до их попадания в бизнес-логику
контроллеров. Основная задача — преобразование и проверка данных
согласно описанным правилам DTO-классов.
В типичной цепочке обработки запросов:
Таким образом, ValidationPipe выступает как фильтр,
обеспечивающий структурную целостность данных.
Наиболее распространённый сценарий — подключение пайпа на уровне всего приложения:
import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(
new ValidationPipe({
whitelist: true,
transform: true,
forbidNonWhitelisted: true,
}),
);
await app.listen(3000);
}
bootstrap();
В этом режиме каждый входящий запрос проходит через единый механизм проверки, что исключает необходимость дублирования логики в контроллерах.
Опция transform: true включает автоматическое
преобразование входных объектов в экземпляры классов DTO. Это критически
важно для корректной работы декораторов class-validator.
Пример DTO:
import { IsInt, IsString } from 'class-validator';
export class CreateUserDto {
@IsString()
name: string;
@IsInt()
age: number;
}
Без трансформации входные данные остаются обычными объектами, что ограничивает возможности валидации и типизации.
С включённой трансформацией:
@Post()
create(@Body() dto: CreateUserDto) {
return dto;
}
dto становится экземпляром CreateUserDto,
что позволяет корректно применять правила декораторов.
Опция whitelist: true автоматически удаляет все
свойства, которые не описаны в DTO-классе.
new ValidationPipe({
whitelist: true,
});
Пример поведения:
Входные данные:
{
"name": "Alex",
"age": 30,
"role": "admin"
}
DTO не содержит role, поэтому поле будет удалено до
передачи в контроллер.
Опция forbidNonWhitelisted: true усиливает поведение
whitelist. Вместо удаления лишних полей генерируется ошибка
валидации.
new ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
});
При наличии лишних свойств выполнение запроса прерывается с ошибкой
BadRequestException.
ValidationPipe использует стандартный механизм
исключений NestJS. Ошибки формируются на основе результата
class-validator и возвращаются в структурированном виде.
Пример ответа:
{
"statusCode": 400,
"message": [
"name must be a string",
"age must be an integer number"
],
"error": "Bad Request"
}
Для изменения структуры ошибок используется
exceptionFactory:
new ValidationPipe({
exceptionFactory: (errors) => {
return new Error(
JSON.stringify(
errors.map(err => ({
property: err.property,
constraints: err.constraints,
})),
),
);
},
});
Этот механизм позволяет унифицировать формат ошибок под требования внешних API или фронтенда.
При включённой трансформации можно использовать дополнительные параметры:
new ValidationPipe({
transform: true,
transformOptions: {
enableImplicitConversion: true,
},
});
enableImplicitConversion позволяет автоматически
преобразовывать примитивы:
Это особенно важно при обработке query-параметров HTTP-запросов, где все значения приходят в виде строк.
ValidationPipe одинаково применяется ко всем источникам
данных:
@Get(':id')
findOne(@Param() params: GetUserParamsDto) {
return params;
}
@Get()
search(@Query() query: SearchDto) {
return query;
}
@Post()
create(@Body() body: CreateUserDto) {
return body;
}
Каждый DTO валидируется независимо, но через единый механизм пайпа.
Помимо глобальной настройки, пайп может применяться точечно:
@Post()
create(
@Body(new ValidationPipe()) dto: CreateUserDto,
) {
return dto;
}
Локальное использование применяется при необходимости различной логики валидации для разных маршрутов.
ValidationPipe поддерживает глубокую валидацию вложенных структур при
использовании @ValidateNested() и @Type():
import { Type } from 'class-transformer';
import { ValidateNested } from 'class-validator';
class AddressDto {
@IsString()
city: string;
}
class UserDto {
@ValidateNested()
@Type(() => AddressDto)
address: AddressDto;
}
Без @Type() вложенная валидация не активируется,
поскольку отсутствует корректная трансформация типов.
Для работы с массивами используется
@Type(() => Class):
class CreateUsersDto {
@ValidateNested({ each: true })
@Type(() => CreateUserDto)
users: CreateUserDto[];
}
Каждый элемент массива проходит отдельную проверку.
Использование ValidationPipe добавляет вычислительную нагрузку из-за:
Оптимизация достигается через:
Наиболее частые проблемы связаны с:
emitDecoratorMetadata в TypeScript@Type() для вложенных объектовКаждая из этих ошибок приводит к тому, что валидация либо не срабатывает, либо работает частично.
ValidationPipe формирует основу строгого контрактного подхода к данным. DTO становятся единственным источником правды о структуре входящих данных, а pipeline обработки запроса превращается в последовательность предсказуемых преобразований и проверок.