Валидация данных в приложениях на JavaScript с использованием class-validator строится на декларативных правилах, описанных через декораторы. Однако сами по себе декораторы не решают задачу понимания этих правил в контексте команды, поддержки кода и предсказуемого поведения API. Документирование превращает набор технических ограничений в формализованную спецификацию поведения данных.
Документирование правил валидации охватывает несколько уровней:
Каждый декоратор в class-validator фактически уже является частью документации. Например:
import { IsString, Length, IsOptional } from "class-validator";
export class CreateUserDto {
@IsString()
@Length(3, 20)
username: string;
@IsOptional()
@IsString()
bio?: string;
}
В этом фрагменте:
@IsString() фиксирует тип данных;@Length(3, 20) задаёт диапазон допустимой длины;@IsOptional() определяет необязательность поля.Однако без дополнительного контекста такие правила остаются полуформальными: они описывают «как проверять», но не «почему именно так».
Одним из ключевых элементов документирования являются сообщения об ошибках, поскольку именно они формируют интерфейс взаимодействия с системой.
В class-validator сообщения задаются через
ValidationOptions:
import { IsEmail } from "class-validator";
export class UserDto {
@IsEmail({}, {
message: "Email должен соответствовать формату user@example.com"
})
email: string;
}
Сообщение становится частью контракта:
Сообщения могут зависеть от параметров:
import { Length } from "class-validator";
export class ProductDto {
@Length(5, 100, {
message: ({ constraints }) =>
`Название должно быть от ${constraints[0]} до ${constraints[1]} символов`
})
title: string;
}
Здесь constraints становятся частью документированного
контракта валидатора.
В class-validator доступен доступ к
ValidationArguments, который позволяет формировать
контекстно-зависимые описания:
import { registerDecorator, ValidationArguments } from "class-validator";
function IsEven() {
return function (object: Object, propertyName: string) {
registerDecorator({
name: "isEven",
target: object.constructor,
propertyName,
validator: {
validate(value: any) {
return typeof value === "number" && value % 2 === 0;
},
defaultMessage(args: ValidationArguments) {
return `Значение ${args.property} должно быть чётным числом`;
}
}
});
};
}
Такой подход позволяет:
defaultMessage;args.property);Группы (groups) позволяют разделять правила в
зависимости от сценария использования DTO. Это важный инструмент
документирования контекстов.
import { IsString, Length } from "class-validator";
export class UpdateUserDto {
@IsString({ groups: ["update"] })
@Length(3, 20, { groups: ["create", "update"] })
username: string;
}
Здесь документируются сразу два аспекта:
Группы фактически описывают жизненный цикл данных.
Условия через ValidateIf фиксируют зависимости между
полями:
import { ValidateIf, IsString } from "class-validator";
export class ProfileDto {
@ValidateIf(o => o.isPublic === true)
@IsString()
displayName: string;
isPublic: boolean;
}
Документирование здесь выражается через:
displayName → isPublic;Такие конструкции особенно важны в сложных DTO, где поведение полей связано.
Кастомные валидаторы в class-validator часто становятся повторно используемыми бизнес-правилами. Их документирование должно быть автономным.
import { ValidatorConstraint, ValidatorConstraintInterface } from "class-validator";
@ValidatorConstraint({ name: "isPhoneNumber", async: false })
export class IsPhoneNumberConstraint implements ValidatorConstraintInterface {
validate(value: string) {
return /^\+?[0-9]{10,15}$/.test(value);
}
defaultMessage() {
return "Номер телефона должен содержать от 10 до 15 цифр";
}
}
Такая реализация фиксирует:
Документирование усиливается при выносе правил в отдельные сущности:
import { IsString, Length } from "class-validator";
export const UsernameRules = [
IsString(),
Length(3, 20)
];
Использование:
export class UserDto {
@UsernameRules
username: string;
}
(в реальной практике чаще применяются фабрики декораторов или функции-обёртки)
Централизация позволяет:
Сама структура DTO уже является формой документации:
export class OrderDto {
productId: number;
quantity: number;
comment?: string;
}
Добавление валидаторов превращает DTO в формальную спецификацию:
Таким образом, DTO становится одновременно моделью данных и документацией API.
Одним из критических аспектов документирования является единообразие сообщений.
Рекомендуемая структура сообщений:
args.property;Пример стандартизации:
message: ({ property }) =>
`Поле ${property} содержит недопустимое значение`
Такой подход обеспечивает:
class-validator позволяет передавать
context, который может использоваться для документирования
бизнес-сценариев:
validate(value, args) {
const context = args?.constraints?.[0];
return context === "strict" ? value > 10 : value >= 10;
}
Контекст в данном случае становится частью описания поведения:
При росте сложности DTO важно фиксировать взаимодействие правил:
@ValidateIf(o => o.type === "premium")
@Length(10, 200)
description: string;
Такие конструкции документируют:
Комбинация декораторов, сообщений, групп и кастомных правил формирует единый слой спецификации:
В результате DTO превращается в формализованную декларацию правил обработки данных, где каждый элемент выполняет роль документационного фрагмента.