@IsNotEmpty

Декоратор @IsNotEmpty из библиотеки class-validator предназначен для проверки значения на пустоту. Валидатор отклоняет значения:

  • '' — пустая строка;
  • null;
  • undefined.

Во всех остальных случаях проверка считается успешной.


Подключение библиотеки

Установка:

npm install class-validator class-transformer

Базовая настройка:

import 'reflect-metadata';

В tsconfig.json должны быть включены параметры:

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

Базовое использование

import { IsNotEmpty } from 'class-validator';

export class CreateUserDto {
  @IsNotEmpty()
  username: string;
}

Проверка:

import { validate } from 'class-validator';

const dto = new CreateUserDto();
dto.username = '';

const errors = await validate(dto);

console.log(errors);

Результат:

[
  {
    property: 'username',
    constraints: {
      isNotEmpty: 'username should not be empty'
    }
  }
]

Какие значения считаются пустыми

Пустая строка

dto.username = '';

Ошибка валидации возникнет.


null

dto.username = null;

Проверка не пройдет.


undefined

dto.username = undefined;

Валидация завершится ошибкой.


Какие значения считаются допустимыми

Строка с текстом

dto.username = 'admin';

Проверка успешна.


Строка с пробелами

dto.username = '   ';

Важная особенность: @IsNotEmpty считает такую строку валидной, потому что строка не является пустой технически.


Число

dto.count = 0;

Значение 0 не считается пустым.


false

dto.isActive = false;

Булево значение false проходит проверку.


Отличие @IsNotEmpty от @IsDefined

Декоратор @IsDefined проверяет только наличие значения:

@IsDefined()
name: string;

Он запрещает:

  • null
  • undefined

Но разрешает:

''

@IsNotEmpty работает строже и дополнительно запрещает пустую строку.


Отличие @IsNotEmpty от @IsEmpty

@IsNotEmpty

Требует, чтобы значение существовало и не было пустым.

@IsNotEmpty()
title: string;

@IsEmpty

Наоборот, требует отсутствия значения.

@IsEmpty()
deletedAt: null;

Проверка нескольких полей

import { IsNotEmpty } from 'class-validator';

export class RegisterDto {
  @IsNotEmpty()
  login: string;

  @IsNotEmpty()
  password: string;

  @IsNotEmpty()
  email: string;
}

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

@IsNotEmpty редко применяется в одиночку. Обычно он комбинируется с другими декораторами.


Совместно с @IsString

import { IsNotEmpty, IsString } from 'class-validator';

export class CreateCategoryDto {
  @IsString()
  @IsNotEmpty()
  name: string;
}

Проверка выполняется в два этапа:

  1. Значение должно быть строкой.
  2. Строка не должна быть пустой.

Совместно с @Length

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

export class CreatePostDto {
  @IsNotEmpty()
  @Length(10, 200)
  title: string;
}

Ограничения:

  • строка обязательна;
  • минимальная длина — 10 символов;
  • максимальная длина — 200 символов.

Совместно с @IsEmail

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

export class AuthDto {
  @IsNotEmpty()
  @IsEmail()
  email: string;
}

Порядок декораторов

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

Распространенная практика:

@IsString()
@IsNotEmpty()
@Length(3, 20)
username: string;

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


Пользовательские сообщения об ошибках

Простое сообщение

@IsNotEmpty({
  message: 'Имя пользователя обязательно'
})
username: string;

Результат:

{
  isNotEmpty: 'Имя пользователя обязательно'
}

Сообщение через функцию

@IsNotEmpty({
  message: (args) => {
    return `Поле ${args.property} не должно быть пустым`;
  }
})
title: string;

Использование в NestJS

Декоратор особенно часто применяется в NestJS внутри DTO.

Пример DTO

import { IsNotEmpty } from 'class-validator';

export class CreateProductDto {
  @IsNotEmpty()
  name: string;

  @IsNotEmpty()
  description: string;
}

Включение глобальной валидации

import { ValidationPipe } from '@nestjs/common';

app.useGlobalPipes(new ValidationPipe());

После этого входящие HTTP-запросы будут автоматически проверяться.


Проверка тела запроса

Запрос

{
  "name": "",
  "description": "Телефон"
}

Ответ сервера

{
  "statusCode": 400,
  "message": [
    "name should not be empty"
  ],
  "error": "Bad Request"
}

Использование с skipMissingProperties

Параметр skipMissingProperties влияет на поведение валидатора.

validate(dto, {
  skipMissingProperties: true
});

Если свойство отсутствует:

{}

то @IsNotEmpty не будет вызван.

Но если свойство присутствует:

{
  "name": ""
}

валидация завершится ошибкой.


Работа с PartialType

В NestJS часто используется:

PartialType(CreateUserDto)

Все поля становятся необязательными.

Однако если поле передано:

{
  "name": ""
}

@IsNotEmpty всё равно сработает и вернет ошибку.


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

Пустой массив

@IsNotEmpty()
tags: string[];

Особенность: пустой массив [] считается валидным.

Причина — массив не равен:

  • null
  • undefined
  • ''

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

Для массивов лучше использовать:

import { ArrayNotEmpty } from 'class-validator';

export class PostDto {
  @ArrayNotEmpty()
  tags: string[];
}

Теперь:

[]

вызовет ошибку.


Проверка объектов

@IsNotEmpty()
settings: object;

Пустой объект:

{}

считается валидным.

Для более строгой проверки требуется кастомная логика.


Проблема строк из пробелов

Частая ошибка:

title = '     ';

@IsNotEmpty пропустит значение.


Решение через @Transform

Использование class-transformer:

import { Transform } from 'class-transformer';
import { IsNotEmpty } from 'class-validator';

export class CreateArticleDto {
  @Transform(({ value }) => value.trim())
  @IsNotEmpty()
  title: string;
}

Теперь строка:

'    '

превратится в:

''

и валидация завершится ошибкой.


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

import { IsNotEmpty, IsNumber } from 'class-validator';

export class PaymentDto {
  @IsNumber()
  @IsNotEmpty()
  amount: number;
}

Значение:

0

будет валидным.

Если требуется запретить ноль:

import { Min } from 'class-validator';

@Min(1)
amount: number;

Проверка boolean

import { IsBoolean, IsNotEmpty } from 'class-validator';

export class SettingsDto {
  @IsBoolean()
  @IsNotEmpty()
  enabled: boolean;
}

Значение:

false

проходит валидацию.


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

import {
  IsNotEmpty,
  ValidateNested
} from 'class-validator';

import { Type } from 'class-transformer';

class ProfileDto {
  @IsNotEmpty()
  bio: string;
}

class UserDto {
  @ValidateNested()
  @Type(() => ProfileDto)
  profile: ProfileDto;
}

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

const dto = new UserDto();

dto.profile = {
  bio: ''
};

Результат:

[
  {
    property: 'profile',
    children: [
      {
        property: 'bio',
        constraints: {
          isNotEmpty: 'bio should not be empty'
        }
      }
    ]
  }
]

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

Использование @ValidateIf:

import {
  IsNotEmpty,
  ValidateIf
} from 'class-validator';

export class UpdatePasswordDto {
  @ValidateIf(o => o.changePassword)
  @IsNotEmpty()
  newPassword: string;

  changePassword: boolean;
}

Поле newPassword проверяется только при:

changePassword = true

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

import { IsNotEmpty } from 'class-validator';

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

Проверка:

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

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

@IsNotEmpty является синхронным декоратором и не выполняет асинхронных операций. Он лишь проверяет текущее значение свойства.


Внутренний принцип работы

Упрощенная логика валидатора:

value !== '' &&
value !== null &&
value !== undefined

Именно поэтому:

  • 0 проходит проверку;
  • false проходит проверку;
  • [] проходит проверку;
  • {} проходит проверку.

Типичные ошибки

Использование без проверки типа

Неправильно:

@IsNotEmpty()
age: any;

Лучше:

@IsNumber()
@IsNotEmpty()
age: number;

Ожидание проверки пробелов

Ошибка ожидания:

'   '

не считается пустой строкой.

Требуется trim().


Использование для массивов

@IsNotEmpty()
items: string[];

Не гарантирует наличие элементов.

Правильнее:

@ArrayNotEmpty()
items: string[];

Практический пример DTO

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

export class RegisterDto {
  @IsString()
  @IsNotEmpty({
    message: 'Логин обязателен'
  })
  @Length(3, 20)
  login: string;

  @IsEmail()
  @IsNotEmpty({
    message: 'Email обязателен'
  })
  email: string;

  @IsString()
  @IsNotEmpty({
    message: 'Пароль обязателен'
  })
  @Length(8, 64)
  password: string;
}

Когда использовать @IsNotEmpty

Декоратор подходит для:

  • обязательных строковых полей;
  • DTO авторизации;
  • DTO регистрации;
  • параметров API;
  • форм ввода;
  • проверок данных перед сохранением;
  • валидации команд и событий;
  • проверки payload в микросервисах.

Когда @IsNotEmpty недостаточно

Требуются дополнительные валидаторы, если необходимо:

Задача Валидатор
Проверка типа строки @IsString()
Проверка email @IsEmail()
Проверка длины @Length()
Проверка массива @ArrayNotEmpty()
Проверка числа @IsNumber()
Проверка минимального значения @Min()
Проверка объекта кастомный валидатор
Удаление пробелов @Transform()