Pipe в NestJS — это шаг перед обработчиком, который преобразует или проверяет входные данные. На pipe-ах держится граница доверия сервиса: до контроллера данные уже либо приведены к нужному типу и провалидированы, либо запрос отклонён. Понимать pipe-ы — значит понимать, что именно проверяется на входе и где проходит граница между форматом и бизнес-правилом.
Что такое pipe
Pipe получает значение и либо возвращает его (возможно, преобразованным), либо бросает исключение. Встроенные pipe-ы решают частые задачи — например, ParseIntPipe превращает строку в число.
import { Controller, Get, Param, ParseIntPipe } from '@nestjs/common';
@Controller('products')
export class ProductController {
@Get(':id')
async getOne(@Param('id', ParseIntPipe) id: number) { /* id уже number */ }
}
Если в пути не число, ParseIntPipe отклонит запрос до входа в метод. Это и есть суть pipe: проверка и приведение — на границе, а не в теле обработчика.
ValidationPipe и DTO
Главный pipe — ValidationPipe: он валидирует тело запроса по DTO-классу, размеченному декораторами class-validator. Включают его глобально на старте.
import { ValidationPipe } from '@nestjs/common';
app.useGlobalPipes(
new ValidationPipe({ whitelist: true, transform: true }),
);
import { IsString, IsInt, Min, IsOptional, MaxLength } from 'class-validator';
export class CreateProductDto {
@IsString()
@MaxLength(200)
name: string;
@IsInt()
@Min(1)
price: number;
@IsOptional()
@IsString()
category?: string;
}
Теперь любой контроллер, принявший @Body() body: CreateProductDto, получит уже проверенный объект; нарушение даст 400 с описанием полей. Опция whitelist: true срезает поля, не объявленные в DTO (клиент не подсунет лишнее), transform: true приводит типы и превращает тело в экземпляр класса через class-transformer.
Кастомный pipe
Когда нужно своё преобразование или проверка, пишут pipe, реализуя PipeTransform.
import { PipeTransform, Injectable, BadRequestException } from '@nestjs/common';
@Injectable()
export class TrimPipe implements PipeTransform {
transform(value: unknown): unknown {
return typeof value === 'string' ? value.trim() : value;
}
}
Кастомные pipe-ы применяют точечно (на параметре или методе) — для общей валидации DTO хватает ValidationPipe.
Граница формата и правила
Важно не путать два уровня проверки. Формат — длина строки, диапазон числа, обязательность поля — это pipe и DTO, на границе. Бизнес-правило — можно ли вообще создать такой продукт в текущем состоянии системы, не занято ли имя — это Handler и домен, не DTO.
Смешивать их — частая ошибка: когда в DTO-валидатор тянут обращение к базе, граница размывается, а правило оказывается в неожиданном месте. DTO отвечает на «правильной ли формы пришли данные», домен — на «допустима ли эта операция». Это та же дисциплина, что Bean Validation против доменных правил в Spring-биндинге: pipe чистит вход, exception filters переводят отказ в ответ, а смысл операции остаётся Handler-у. Чёткая граница на входе — то, что позволяет продукт-инженеру доверять данным внутри сервиса.