Что такое class-validator

Class-validator — библиотека валидации данных для JavaScript и TypeScript

Class-validator представляет собой библиотеку, предназначенную для декларативной валидации объектов с использованием классов и декораторов. Основная идея заключается в том, чтобы описывать правила проверки данных прямо в модели, а не выносить их в отдельные функции или схемы. Такой подход делает код более структурированным и ближе к предметной области, особенно в проектах, где активно используются классы и TypeScript.

Библиотека активно применяется в экосистеме Node.js, особенно в связке с TypeScript и фреймворками, ориентированными на объектно-ориентированную модель. Её ключевая особенность — использование декораторов, позволяющих «прикреплять» правила валидации непосредственно к свойствам классов.


В основе class-validator лежит идея отражения метаданных о свойствах классов и последующей их проверки в рантайме. Поскольку JavaScript сам по себе не хранит типы и аннотации, библиотека использует механизм reflect-metadata, который позволяет сохранять дополнительную информацию о классах и их свойствах.

Каждое правило валидации реализуется как декоратор. При вызове функции проверки библиотека:

  • считывает метаданные объекта;
  • извлекает список правил для каждого свойства;
  • выполняет проверки последовательно;
  • формирует список ошибок, если они есть.

Таким образом, валидация становится частью модели данных.


Установка и подключение

Библиотека устанавливается через npm:

npm install class-validator reflect-metadata

Для корректной работы декораторов необходимо включить поддержку в TypeScript:

{
  "experimentalDecorators": true,
  "emitDecoratorMetadata": true
}

Также важно импортировать reflect-metadata один раз в точке входа приложения:

import "reflect-metadata";

Определение классов с правилами валидации

Основной способ использования — создание классов DTO (Data Transfer Object), где каждое поле описывается вместе с набором ограничений.

import { IsString, IsInt, MinLength, MaxLength } from "class-validator";

class User {
  @IsString()
  name;

  @IsInt()
  age;

  @IsString()
  @MinLength(5)
  @MaxLength(20)
  password;
}

В данном примере каждое свойство получает набор декораторов, описывающих правила проверки.


Выполнение валидации

Для проверки объекта используется функция validate:

import { validate } from "class-validator";

const user = new User();
user.name = 123;
user.age = "old";
user.password = "123";

validate(user).then(errors => {
  console.log(errors);
});

Результатом является массив ошибок. Если массив пустой, объект считается валидным.

Каждая ошибка содержит:

  • имя свойства;
  • список ограничений;
  • сообщения об ошибках;
  • значение, которое не прошло проверку.

Декораторы и их категории

Библиотека предоставляет широкий набор встроенных декораторов, которые можно разделить на несколько групп.

Проверка типов

Используются для проверки базовых типов данных:

  • @IsString()
  • @IsNumber()
  • @IsBoolean()
  • @IsDate()

Эти декораторы обеспечивают базовую типизацию входящих данных.


Проверка строк

Специализированные проверки строковых значений:

  • @MinLength(length)
  • @MaxLength(length)
  • @Matches(pattern)
  • @Contains(value)
  • @IsEmail()

Пример:

class Profile {
  @IsEmail()
  email;

  @Matches(/^[a-zA-Z]+$/)
  username;
}

Проверка чисел

Для числовых значений доступны ограничения диапазонов:

  • @Min(value)
  • @Max(value)
  • @IsPositive()
  • @IsNegative()

Эти правила позволяют ограничивать допустимые диапазоны значений.


Проверка массивов

Работа с массивами включает проверку структуры и содержимого:

  • @IsArray()
  • @ArrayMinSize(n)
  • @ArrayMaxSize(n)
  • @ArrayNotEmpty()
  • @ArrayUnique()

Дополнительно можно проверять тип элементов внутри массива:

class Group {
  @IsArray()
  @IsString({ each: true })
  tags;
}

Проверка вложенных объектов

Одной из ключевых возможностей class-validator является поддержка вложенных структур.

import { ValidateNested } from "class-validator";
import { Type } from "class-transformer";

class Address {
  @IsString()
  city;

  @IsString()
  street;
}

class User {
  @ValidateNested()
  @Type(() => Address)
  address;
}

Здесь важно использование class-transformer, который помогает корректно преобразовывать вложенные объекты в экземпляры классов.


Кастомные валидаторы

Когда встроенных правил недостаточно, можно создавать собственные валидаторы.

Реализация интерфейса ValidatorConstraint

import { ValidatorConstraint, ValidatorConstraintInterface } from "class-validator";

@ValidatorConstraint({ name: "isEven", async: false })
class IsEvenConstraint {
  validate(value) {
    return typeof value === "number" && value % 2 === 0;
  }

  defaultMessage() {
    return "Значение должно быть чётным числом";
  }
}

Использование кастомного декоратора

import { Validate } from "class-validator";

class NumberModel {
  @Validate(IsEvenConstraint)
  value;
}

Кастомные валидаторы позволяют расширять систему под любые бизнес-правила.


Асинхронная валидация

Некоторые проверки требуют обращения к базе данных или внешним сервисам. Class-validator поддерживает асинхронные валидаторы.

@ValidatorConstraint({ async: true })
class IsUserAlreadyExist {
  async validate(email) {
    const user = await database.findUser(email);
    return !user;
  }
}

Асинхронные проверки часто используются для уникальности значений, например email или username.


Группы валидации

Библиотека поддерживает условную валидацию через группы. Это позволяет применять разные наборы правил в зависимости от контекста.

class User {
  @IsString({ groups: ["create"] })
  password;

  @IsString({ groups: ["update"] })
  id;
}

При вызове validate можно указать группу:

validate(user, { groups: ["create"] });

Условная валидация

С помощью @ValidateIf можно задавать условия выполнения проверок:

class User {
  @ValidateIf(o => o.isActive)
  @IsString()
  status;
}

Это позволяет гибко управлять логикой валидации без усложнения структуры классов.


Трансформация и подготовка данных

Хотя class-validator отвечает за проверку, он часто используется вместе с class-transformer, который выполняет преобразование «сырых» объектов в экземпляры классов.

import { plainToInstance } from "class-transformer";

const user = plainToInstance(User, requestBody);

После этого можно безопасно запускать валидацию.


Ошибки валидации и их структура

Каждая ошибка представляет собой объект со следующей структурой:

  • property — имя поля;
  • constraints — список нарушенных правил;
  • value — переданное значение;
  • children — ошибки вложенных объектов.

Пример анализа ошибок позволяет строить детализированные сообщения для пользователей или логирования.


Архитектурные особенности

Class-validator построен на нескольких ключевых принципах:

  • декларативность описания правил;
  • использование метаданных через reflect-metadata;
  • расширяемость через кастомные валидаторы;
  • поддержка синхронных и асинхронных проверок;
  • ориентация на классовую модель данных.

Такой подход делает библиотеку удобной для приложений средней и высокой сложности, где важно разделение структуры данных и логики их проверки.


Ограничения и особенности поведения

Несмотря на гибкость, библиотека имеет ряд особенностей:

  • требует включения экспериментальных возможностей TypeScript;
  • зависит от reflect-metadata;
  • не работает напрямую с примитивными объектами без классов;
  • требует дополнительного шага трансформации для вложенных структур.

Эти особенности важно учитывать при проектировании архитектуры приложения, чтобы избежать некорректной валидации или потери типов.