Метод validateSync для синхронной валидации

Синхронная модель валидации и область применения

Метод validateSync реализует синхронный запуск системы валидации, основанной на декораторах и метаданных. Его ключевая особенность заключается в полном отсутствии асинхронного ожидания: вся проверка выполняется немедленно в пределах текущего вызова функции.

Синхронная валидация применяется в сценариях, где:

  • отсутствуют асинхронные валидаторы (@Validate, @ValidatorConstraint с промисами);
  • не требуется обращение к внешним сервисам (БД, HTTP, кеш);
  • необходима предсказуемая и мгновенная проверка входных данных;
  • выполняется валидация DTO до попадания в бизнес-логику.

Сигнатура метода

validateSync(object: object, options?: ValidatorOptions): ValidationError[]

Возвращаемое значение всегда представляет собой массив ошибок валидации:

ValidationError[]

Пустой массив означает, что объект прошёл проверку без нарушений.

Базовый механизм работы

При вызове validateSync библиотека выполняет следующие шаги:

  • извлекает метаданные, созданные декораторами class-validator;
  • проходит по каждому полю объекта;
  • применяет синхронные валидаторы;
  • формирует структуру ValidationError при нарушениях;
  • рекурсивно обрабатывает вложенные объекты при включённой опции validateNested.

Асинхронные ограничения полностью игнорируются, что делает поведение метода строго детерминированным.

Структура ValidationError

Каждая ошибка содержит следующую информацию:

interface ValidationError {
  property: string;
  constraints?: { [type: string]: string };
  children?: ValidationError[];
  value?: any;
}
  • property — имя поля;
  • constraints — набор сообщений ошибок;
  • children — вложенные ошибки для объектов;
  • value — фактическое значение.

Пример базового использования

import { validateSync, IsString, Length } from "class-validator";

class UserDto {
  @IsString()
  name: string;

  @Length(5, 20)
  password: string;
}

const user = new UserDto();
user.name = 123 as any;
user.password = "123";

const errors = validateSync(user);

console.log(errors);

В результате будет сформирован массив ошибок для обоих полей.

Отличие от validate

Основное различие заключается в модели выполнения:

Характеристика validate validateSync
Асинхронность есть отсутствует
Поддержка async-валидаторов да нет
Возвращаемое значение Promise<ValidationError[]> ValidationError[]
Использование I/O возможно невозможно

validateSync всегда выполняется мгновенно и не требует обработки промисов.

Поддерживаемые типы валидаторов

Синхронный режим поддерживает:

  • все встроенные синхронные декораторы (@IsString, @IsInt, @Length, @Min, @Max);
  • кастомные синхронные валидаторы;
  • преобразования, если они не зависят от асинхронных операций.

Асинхронные ограничения, например проверки уникальности в базе данных, в этом режиме не выполняются.

Поведение кастомных валидаторов

Кастомные валидаторы работают только при синхронной реализации:

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

@ValidatorConstraint()
class IsEvenConstraint implements ValidatorConstraintInterface {
  validate(value: number) {
    return value % 2 === 0;
  }
}

Если validate возвращает Promise, такой валидатор не будет корректно учитываться в validateSync.

Вложенные объекты

Для корректной работы вложенной валидации требуется использование @ValidateNested:

import { ValidateNested } from "class-validator";

class Address {
  @IsString()
  city: string;
}

class User {
  @ValidateNested()
  address: Address;
}

При вызове:

validateSync(user);

вложенные ошибки будут помещены в children соответствующего ValidationError.

Поведение опций ValidatorOptions

Метод принимает ограниченный набор опций:

interface ValidatorOptions {
  skipMissingProperties?: boolean;
  whitelist?: boolean;
  forbidNonWhitelisted?: boolean;
  forbidUnknownValues?: boolean;
}
skipMissingProperties

Игнорирует отсутствующие поля:

validateSync(user, { skipMissingProperties: true });
whitelist

Удаляет свойства, не имеющие декораторов:

validateSync(user, { whitelist: true });
forbidNonWhitelisted

Генерирует ошибку при наличии лишних полей:

validateSync(user, { forbidNonWhitelisted: true });
forbidUnknownValues

Отклоняет объекты без метаданных валидации:

validateSync({}, { forbidUnknownValues: true });

Рекурсивная обработка и глубина проверки

Система валидации проходит по структуре объекта рекурсивно, формируя дерево ошибок. Глубина зависит от количества вложенных DTO и наличия @ValidateNested.

При сложных структурах:

class Profile {
  @ValidateNested()
  address: Address;

  @ValidateNested()
  settings: Settings;
}

результат содержит иерархию children, отражающую полную структуру объекта.

Ограничения синхронного режима

validateSync накладывает ряд архитектурных ограничений:

  • невозможность проверки уникальности через базу данных;
  • невозможность HTTP-запросов внутри валидаторов;
  • игнорирование Promise-возвращающих валидаторов;
  • невозможность ожидания внешних зависимостей.

По этой причине синхронный режим применяется преимущественно на уровне DTO до входа в слой сервисов.

Преобразование ошибок в плоский формат

Для упрощённой обработки структура ValidationError может быть преобразована в список сообщений:

const flatErrors = validateSync(user).flatMap(e =>
  Object.values(e.constraints ?? {})
);

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

Поведение при пустых объектах

При включённой опции:

forbidUnknownValues: true

передача пустого объекта:

validateSync({})

приводит к генерации ошибки уровня объекта, поскольку отсутствуют метаданные классовой структуры.

Взаимодействие с трансформацией объектов

validateSync не выполняет автоматическую трансформацию типов. При необходимости приведения типов используется class-transformer перед валидацией:

import { plainToInstance } from "class-transformer";

const dto = plainToInstance(UserDto, plainObject);
validateSync(dto);

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

Особенности производительности

Синхронный режим демонстрирует более высокую скорость выполнения на малых и средних объектах за счёт отсутствия планирования микрозадач и промисов. Однако при увеличении глубины объектов нагрузка возрастает линейно по количеству проверяемых полей и декораторов.

Основная стоимость операций:

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

Типичные сценарии применения

  • проверка DTO на уровне контроллеров;
  • валидация конфигурационных объектов;
  • тестовые окружения без асинхронной инфраструктуры;
  • предварительная проверка данных до бизнес-слоя.