Валидация тела запроса в серверных JavaScript-приложениях строится вокруг идеи строгого описания входных данных и автоматической проверки их соответствия заданным правилам до попадания в бизнес-логику. В экосистеме TypeScript и NestJS наиболее распространённым инструментом становится Class-validator, работающий совместно с class-transformer и механизмами пайпов.
Тело HTTP-запроса рассматривается как неконтролируемый источник данных, требующий формализации структуры. Каждый входной объект приводится к DTO (Data Transfer Object), где свойства описываются через классы и аннотации. Валидация выполняется на уровне экземпляров классов, что позволяет использовать метаданные декораторов и отражение типов.
Основная цель подхода — исключение некорректных данных до выполнения контроллеров и сервисов.
Библиотека устанавливается вместе с зависимостями преобразования объектов:
npm install class-validator class-transformer
В типичном серверном фреймворке подключается механизм автоматической валидации входящих данных через пайпы. На уровне приложения активируется глобальная конфигурация:
app.useGlobalPipes(
new ValidationPipe({
transform: true,
whitelist: true,
forbidNonWhitelisted: true,
}),
);
Каждый параметр влияет на поведение обработки тела запроса:
Структура тела запроса формализуется через класс:
import { IsString, IsInt, MinLength } from 'class-validator';
class CreateUserDto {
@IsString()
@MinLength(3)
username: string;
@IsInt()
age: number;
}
Каждое поле сопровождается набором правил, которые применяются при валидации входящего объекта.
Наиболее часто используемые декораторы:
IsString — проверка строкового типаIsInt — целочисленное значениеIsBoolean — булев типIsEmail — формат emailMinLength / MaxLength — ограничения
длиныIsOptional — необязательное полеHTTP-запрос передаёт данные в виде строк или JSON-структур, где числовые значения часто приходят как строки. Без преобразования типы могут не соответствовать ожидаемым в DTO.
Использование transform совместно с class-transformer позволяет автоматически приводить данные к нужным типам:
import { Type } from 'class-transformer';
import { IsInt } from 'class-validator';
class CreateUserDto {
@Type(() => Number)
@IsInt()
age: number;
}
Механизм преобразования критичен для корректной работы числовых и датированных полей.
Числовые ограничения описываются через специализированные декораторы:
import { IsInt, Min, Max } from 'class-validator';
class ProductDto {
@IsInt()
@Min(1)
@Max(1000)
price: number;
}
Дополнительные ограничения позволяют формализовать бизнес-правила на уровне входных данных, снижая нагрузку на сервисный слой.
Строковые поля часто требуют комплексной проверки:
import { IsString, Length, Matches } from 'class-validator';
class ProfileDto {
@IsString()
@Length(5, 20)
nickname: string;
@Matches(/^[a-zA-Z0-9_]+$/)
username: string;
}
Регулярные выражения позволяют описывать сложные правила формата без необходимости ручной обработки.
Булевы поля часто приходят как строки "true" и
"false". Для корректной интерпретации используется
преобразование:
import { Type } from 'class-transformer';
import { IsBoolean } from 'class-validator';
class SettingsDto {
@Type(() => Boolean)
@IsBoolean()
isActive: boolean;
}
Работа с массивами требует комбинирования декораторов:
import { IsArray, IsString } from 'class-validator';
class TagsDto {
@IsArray()
@IsString({ each: true })
tags: string[];
}
Параметр each: true обеспечивает применение правила к
каждому элементу коллекции.
Тело запроса часто содержит сложные объекты. Для корректной валидации используется вложенная декларация классов:
import { ValidateNested, IsString } from 'class-validator';
import { Type } from 'class-transformer';
class AddressDto {
@IsString()
city: string;
}
class UserDto {
@ValidateNested()
@Type(() => AddressDto)
address: AddressDto;
}
Без ValidateNested вложенные объекты не проходят
полноценную проверку.
Контроль структуры тела запроса реализуется через whitelist-режим. Поля, отсутствующие в DTO, автоматически удаляются либо вызывают ошибку:
new ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
});
Такой подход снижает риск проникновения неожиданных данных в систему.
При нарушении правил формируется массив ошибок, содержащий:
Структура ошибки позволяет централизованно формировать ответ API:
{
statusCode: 400,
message: [
"username must be longer than or equal to 3 characters"
],
error: "Bad Request"
}
Валидационные ошибки возникают до вызова контроллера, что исключает выполнение бизнес-логики при некорректных данных.
При необходимости сложной логики создаются собственные правила через
интерфейс ValidatorConstraint:
import {
ValidatorConstraint,
ValidatorConstraintInterface,
ValidationArguments,
} from 'class-validator';
@ValidatorConstraint({ name: 'isEven', async: false })
class IsEvenConstraint implements ValidatorConstraintInterface {
validate(value: number) {
return value % 2 === 0;
}
defaultMessage(args: ValidationArguments) {
return `${args.property} must be even`;
}
}
Использование:
import { Validate } from 'class-validator';
class NumberDto {
@Validate(IsEvenConstraint)
value: number;
}
Кастомные валидаторы позволяют переносить доменную логику на уровень проверки входных данных.
Несколько декораторов могут комбинироваться на одном поле, формируя цепочку ограничений:
class AccountDto {
@IsString()
@MinLength(5)
@MaxLength(30)
username: string;
}
Каждое правило выполняется последовательно, а итоговая ошибка агрегируется в общий результат валидации.
Необязательные свойства исключаются из проверки при отсутствии значения:
import { IsOptional, IsString } from 'class-validator';
class UpdateUserDto {
@IsOptional()
@IsString()
bio?: string;
}
Такой подход позволяет использовать единый DTO для частичного обновления данных.
При включённом преобразовании данные тела запроса проходят несколько этапов:
Эта цепочка обеспечивает предсказуемую обработку входных данных независимо от исходного формата JSON.
Контроллеры принимают уже валидированные DTO, не выполняя дополнительной проверки:
@Post()
create(@Body() dto: CreateUserDto) {
return this.service.create(dto);
}
Таким образом, слой контроллера остаётся тонким и ориентированным на маршрутизацию, а не на обработку данных.
При росте количества DTO структура валидации начинает играть роль формального контракта API. Повторное использование классов, композиция и наследование позволяют уменьшить дублирование правил:
class BaseUserDto {
@IsString()
username: string;
}
class AdminUserDto extends BaseUserDto {
@IsString()
role: string;
}
Наследование декораторов сохраняется, что упрощает поддержку общей логики валидации.
Class-validator не заменяет систему типов TypeScript, а дополняет её на уровне runtime. Типы обеспечивают статическую проверку, тогда как декораторы формируют поведение во время выполнения. Совместное использование позволяет закрыть оба уровня контроля данных.