Пошук уроків, статей та іншого контенту
Підключите ValidationPipe глобально й локально та налаштуєте whitelist, transform, forbidNonWhitelisted і групи.
ValidationPipeValidationPipe перевіряє вхідні дані перед передаванням їх у метод контролера. Найчастіше він використовується для:
перевірки body, query і params;
видалення невідомих властивостей;
відхилення зайвих властивостей;
перетворення звичайних JavaScript-об’єктів на екземпляри DTO;
вибору набору правил за допомогою груп.
Для роботи ValidationPipe потрібні пакети class-validator і class-transformer:
npm install class-validator class-transformerDTO описується класом із декораторами валідації:
import { IsEmail, IsString, MinLength } from 'class-validator';
export class CreateUserDto {
@IsEmail()
email: string;
@IsString()
@MinLength(8)
password: string;
}ValidationPipeГлобальний pipe підключається у файлі main.ts і застосовується до всіх контролерів застосунку.
import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(
new ValidationPipe({
whitelist: true,
transform: true,
forbidNonWhitelisted: true,
}),
);
await app.listen(3000);
}
bootstrap();Тепер кожен endpoint, який використовує DTO, автоматично перевірятиме вхідні дані.
// create-user.dto.ts
import { IsEmail, IsString, MinLength } from 'class-validator';
export class CreateUserDto {
@IsEmail()
email: string;
@IsString()
@MinLength(8)
password: string;
}// users.controller.ts
import { Body, Controller, Post } from '@nestjs/common';
import { CreateUserDto } from './create-user.dto';
@Controller('users')
export class UsersController {
@Post()
create(@Body() dto: CreateUserDto) {
return {
email: dto.email,
passwordLength: dto.password.length,
};
}
}// app.module.ts
import { Module } from '@nestjs/common';
import { UsersController } from './users.controller';
@Module({
controllers: [UsersController],
})
export class AppModule {}// main.ts
import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(
new ValidationPipe({
whitelist: true,
transform: true,
forbidNonWhitelisted: true,
}),
);
await app.listen(3000);
}
bootstrap();Запит:
POST /users
Content-Type: application/json
{
"email": "user@example.com",
"password": "secret123"
}Буде успішним.
Запит із зайвою властивістю:
POST /users
Content-Type: application/json
{
"email": "user@example.com",
"password": "secret123",
"role": "admin"
}буде відхилений, оскільки role не описана в DTO.
ValidationPipeІноді налаштування потрібні лише для одного контролера або endpoint. У такому разі pipe можна підключити за допомогою @UsePipes().
import {
Body,
Controller,
Post,
UsePipes,
ValidationPipe,
} from '@nestjs/common';
import { CreateUserDto } from './create-user.dto';
@Controller('users')
export class UsersController {
@Post()
@UsePipes(
new ValidationPipe({
whitelist: true,
transform: true,
forbidNonWhitelisted: true,
}),
)
create(@Body() dto: CreateUserDto) {
return dto;
}
}import {
Body,
Controller,
Post,
UsePipes,
ValidationPipe,
} from '@nestjs/common';
import { CreateUserDto } from './create-user.dto';
@Controller('users')
@UsePipes(
new ValidationPipe({
whitelist: true,
transform: true,
}),
)
export class UsersController {
@Post()
create(@Body() dto: CreateUserDto) {
return dto;
}
}Глобальні та локальні pipes можуть застосовуватися разом. Тому важливо не створювати конфліктні налаштування: наприклад, глобальний pipe може вже відхиляти зайві властивості до того, як дані обробить локальний pipe.
whitelistwhitelist: true видаляє з об’єкта всі властивості, які не мають декораторів class-validator.
DTO:
import { IsEmail } from 'class-validator';
export class LoginDto {
@IsEmail()
email: string;
}Вхідні дані:
{
"email": "user@example.com",
"isAdmin": true
}Після валідації значення матиме вигляд:
{
"email": "user@example.com"
}Властивість isAdmin буде видалена.
whitelist працює лише для властивостей, які не мають жодного декоратора валідації. Якщо властивість повинна бути дозволена, але не має окремого правила перевірки, її можна позначити декоратором @Allow():
import { Allow, IsEmail } from 'class-validator';
export class ProfileDto {
@IsEmail()
email: string;
@Allow()
displayName: string;
}forbidNonWhitelistedforbidNonWhitelisted: true не видаляє невідомі властивості мовчки, а повертає помилку.
Для коректної роботи ця опція використовується разом із whitelist: true:
new ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
});Якщо DTO містить лише email, а клієнт надсилає isAdmin, NestJS поверне помилку 400 Bad Request.
Цей режим корисний для API, де важливо явно повідомляти клієнту про неправильну структуру запиту. Без forbidNonWhitelisted зайві властивості просто видаляються.
transformБез transform NestJS передає в метод контролера звичайний JavaScript-об’єкт. З transform: true об’єкт перетворюється на екземпляр відповідного DTO-класу.
new ValidationPipe({
transform: true,
});Це важливо, коли потрібно використовувати методи класу або працювати з типами, описаними в DTO.
transform також дозволяє перетворювати прості параметри маршрутів і query-параметри:
import { Controller, Get, Param, Query } from '@nestjs/common';
@Controller('products')
export class ProductsController {
@Get(':id')
findOne(
@Param('id') id: number,
@Query('limit') limit: number,
) {
return {
id,
idType: typeof id,
limit,
limitType: typeof limit,
};
}
}Для автоматичного перетворення примітивних значень можна використати:
new ValidationPipe({
transform: true,
transformOptions: {
enableImplicitConversion: true,
},
});Тоді значення "42" може бути перетворене на число 42 відповідно до типу параметра.
Для властивостей DTO краще явно описувати перетворення, коли воно потрібне, наприклад за допомогою @Type():
import { Type } from 'class-transformer';
import { IsInt, Min } from 'class-validator';
export class PaginationDto {
@Type(() => Number)
@IsInt()
@Min(1)
page: number;
}Групи дають змогу застосовувати різні правила до одного DTO в різних сценаріях. Наприклад, під час створення користувача поле password обов’язкове, а під час оновлення його можна не передавати.
import {
IsEmail,
IsOptional,
IsString,
MinLength,
} from 'class-validator';
export class UserDto {
@IsEmail({}, { groups: ['create', 'update'] })
email: string;
@IsString({ groups: ['create', 'update'] })
@MinLength(8, { groups: ['create', 'update'] })
@IsOptional({ groups: ['update'] })
password?: string;
}Pipe отримує групи через опцію groups:
new ValidationPipe({
groups: ['create'],
});import {
Body,
Controller,
Patch,
Post,
UsePipes,
ValidationPipe,
} from '@nestjs/common';
import { UserDto } from './user.dto';
@Controller('users')
export class UsersController {
@Post()
@UsePipes(
new ValidationPipe({
transform: true,
whitelist: true,
groups: ['create'],
}),
)
create(@Body() dto: UserDto) {
return dto;
}
@Patch()
@UsePipes(
new ValidationPipe({
transform: true,
whitelist: true,
groups: ['update'],
}),
)
update(@Body() dto: UserDto) {
return dto;
}
}Для POST /users використовується група create, а для PATCH /users — група update.
Усі декоратори, для яких потрібно застосування в певному сценарії, мають отримати відповідну групу:
@IsString({ groups: ['create', 'update'] })
name: string;Якщо декоратор не має потрібної групи, він не бере участі у валідації цього сценарію.
Група, задана в глобальному pipe, буде застосовуватися до всіх endpoint:
app.useGlobalPipes(
new ValidationPipe({
groups: ['create'],
}),
);Таке налаштування підходить лише тоді, коли одна група справді потрібна всьому застосунку. Якщо група залежить від endpoint, її краще передавати в локальному ValidationPipe.
Також потрібно пам’ятати, що глобальний і локальний pipes не замінюють один одного. Якщо вони обидва виконують валідацію, кожен із них застосовує власні правила.
За замовчуванням ValidationPipe повертає помилку з HTTP-статусом 400, якщо вхідні дані не відповідають DTO.
Наприклад, для такого DTO:
import { IsEmail, MinLength } from 'class-validator';
export class CreateUserDto {
@IsEmail()
email: string;
@MinLength(8)
password: string;
}запит:
{
"email": "incorrect-email",
"password": "123"
}буде відхилений через неправильну електронну адресу та недостатню довжину пароля.
transformЯкщо очікується екземпляр DTO або перетворення типів, але transform не ввімкнено, у метод контролера потрапить звичайний об’єкт.
new ValidationPipe({
transform: true,
});forbidNonWhitelisted без whitelistНалаштування:
new ValidationPipe({
forbidNonWhitelisted: true,
});не має очікуваного сенсу без:
whitelist: trueВикористовуйте їх разом.
За whitelist: true властивість без декоратора буде видалена. Якщо вона потрібна в DTO, додайте правило валідації або @Allow().
Такий тип:
limit: number;не перевіряє вхідне значення сам по собі. Потрібні ValidationPipe і декоратори class-validator, наприклад @IsInt().
Глобальний pipe теж бере участь у валідації endpoint. Якщо глобальна конфігурація використовує одні групи або суворіші правила, локальний pipe не скасовує їх автоматично.
ValidationPipe перевіряє вхідні дані відповідно до DTO.
app.useGlobalPipes() підключає pipe для всього застосунку.
@UsePipes() дає змогу налаштувати pipe для контролера або окремого методу.
whitelist: true видаляє властивості, яких немає в DTO.
forbidNonWhitelisted: true перетворює наявність таких властивостей на помилку.
transform: true перетворює об’єкти на екземпляри DTO і дає змогу виконувати перетворення типів.
groups дозволяє використовувати різні правила валідації для різних сценаріїв.