Параметр groups

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


Концепция групповой валидации

Группы валидации представляют собой механизм, позволяющий назначать отдельные правила проверки для разных контекстов использования объекта.

Каждый декоратор валидатора может принимать параметр:

  • groups: string[]

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

Если группы не указаны ни у одного декоратора, такие правила относятся к группе по умолчанию и выполняются всегда (при отсутствии ограничения через groups в вызове валидации).


Базовый синтаксис использования групп

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

import { IsEmail, IsNotEmpty, Length } from 'class-validator';

export class UserDto {
  @IsEmail({}, { groups: ['create'] })
  email: string;

  @IsNotEmpty({ groups: ['create', 'update'] })
  name: string;

  @Length(6, 20, { groups: ['create'] })
  password: string;
}

В этом примере:

  • email проверяется только при создании
  • name проверяется при создании и обновлении
  • password проверяется только при создании

Запуск валидации с указанием групп

Чтобы активировать конкретную группу правил, используется параметр groups в функции validate или validateOrReject:

import { validate } from 'class-validator';

const dto = new UserDto();
dto.email = 'test@mail.com';

validate(dto, { groups: ['create'] });

При указании groups: ['create'] будут применены только те валидаторы, которые помечены этой группой.


Поведение без указания групп

Если валидация выполняется без параметра groups, библиотека применяет следующие правила:

  • срабатывают только валидаторы без параметра groups
  • валидаторы с указанными группами игнорируются
validate(dto);

В этом случае свойства, ограниченные группами, не участвуют в проверке.


Несколько групп одновременно

Один валидатор может принадлежать нескольким группам одновременно:

@IsNotEmpty({ groups: ['create', 'update', 'admin'] })
username: string;

При вызове:

validate(dto, { groups: ['update'] });

валидатор будет активирован, поскольку update входит в список групп.


Разделение сценариев: create / update

Наиболее распространённый сценарий использования групп — разделение логики создания и обновления сущности.

export class ProductDto {
  @IsNotEmpty({ groups: ['create'] })
  title: string;

  @IsOptional({ groups: ['update'] })
  @IsNotEmpty({ groups: ['create'] })
  description: string;

  @IsNumber({}, { groups: ['create'] })
  price: number;
}

Логика:

  • при создании обязательны все поля
  • при обновлении допускается частичное обновление

Взаимодействие с @ValidateNested и вложенными объектами

Группы работают и с вложенной валидацией, но требуют явного указания:

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

class AddressDto {
  @IsNotEmpty({ groups: ['create'] })
  city: string;
}

class UserDto {
  @ValidateNested({ groups: ['create'] })
  @Type(() => AddressDto)
  address: AddressDto;
}

Без указания групп на уровне ValidateNested вложенные проверки могут быть пропущены при использовании групповой фильтрации.


Группы и @IsOptional

Комбинация groups и @IsOptional требует аккуратности, так как поведение зависит от активной группы.

@IsOptional({ groups: ['update'] })
@IsString({ groups: ['update'] })
nickname?: string;

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


Приоритет групп над стандартной валидацией

При наличии группового ограничения:

  • декораторы без groups игнорируются при указании groups валидации
  • только совпадающие группы участвуют в проверке
@IsNotEmpty()
@IsEmail({ groups: ['create'] })
email: string;

Поведение:

  • без groups → срабатывает IsNotEmpty
  • с groups: ['create'] → срабатывает IsEmail, IsNotEmpty игнорируется

Частичное пересечение групп

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

validate(dto, { groups: ['create', 'admin'] });

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


Типичные архитектурные паттерны

DTO с разделением ответственности

class BaseUserDto {
  @IsNotEmpty({ groups: ['create', 'update'] })
  username: string;
}

class CreateUserDto extends BaseUserDto {
  @IsEmail({ groups: ['create'] })
  email: string;
}

class UpdateUserDto extends BaseUserDto {
  @IsOptional({ groups: ['update'] })
  email?: string;
}

Использование групп для ролей доступа

Группы могут моделировать не только CRUD-сценарии, но и уровни доступа:

class DocumentDto {
  @IsNotEmpty({ groups: ['user', 'admin'] })
  title: string;

  @IsString({ groups: ['admin'] })
  internalNote: string;
}

Ограничения механизма groups

  • отсутствие строгой типизации групп (строки без контроля компилятора)
  • необходимость синхронизации групп между DTO и вызовом validate
  • сложность при большом количестве пересекающихся сценариев
  • ошибки из-за забытых групп в вложенных валидаторах

Особенности поведения в runtime

  • группы сравниваются строго по строковому совпадению
  • порядок групп не имеет значения
  • пустой массив groups: [] эквивалентен отсутствию группы
  • если groups передан в validate, но не указан в декораторах — правила не активируются

Сочетание с ValidationPipe (NestJS)

В архитектуре NestJS параметр групп часто передаётся через пайп:

new ValidationPipe({
  groups: ['create'],
});

Это приводит к глобальной активации групповой логики для входящих DTO без явного вызова validate.


Поведение при конфликте групп

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

@IsString({ groups: ['create', 'update'] })
name: string;
validate(dto, { groups: ['update'] });

Результат: валидатор применяется.


Использование групп в сложных моделях

В больших моделях группировка часто применяется для:

  • разделения публичных и внутренних полей
  • разграничения сценариев API
  • контроля частичного обновления ресурсов
  • управления административными и пользовательскими режимами
class AccountDto {
  @IsEmail({ groups: ['public'] })
  email: string;

  @IsString({ groups: ['admin'] })
  internalId: string;

  @IsBoolean({ groups: ['admin'] })
  isBanned: boolean;
}

Поведение при отсутствии совпадений

Если ни один валидатор не соответствует переданным группам:

  • объект считается валидным
  • ошибки не генерируются даже при наличии декораторов

Это поведение делает группы мощным инструментом, но требует строгого контроля конфигурации.