Pipes представляют собой промежуточный слой в цепочке обработки входящих данных. Их основная задача заключается в трансформации и валидации аргументов, поступающих в контроллеры. Они работают до вызова метода обработчика маршрута и позволяют централизованно управлять качеством входных данных.
Pipes применяются к:
@Param)@Query)@Body)@Headers)Ключевая особенность заключается в том, что pipes выполняются до бизнес-логики, что позволяет отделить проверку данных от основной функциональности.
Любой pipe реализует интерфейс PipeTransform,
определяющий единый контракт обработки:
interface PipeTransform<T = any, R = any> {
transform(value: T, metadata: ArgumentMetadata): R;
}
Где:
value — входное значениеmetadata — информация о параметре (тип, имя,
источник)R — результат преобразования или исключениеСтруктура ArgumentMetadata:
type:
"body" | "query" | "param" | "custom"metatype: тип данных (например, класс DTO)data: имя параметраNestJS предоставляет набор стандартных pipes для типовых задач.
Используется для преобразования строки в число:
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {
return this.service.findOne(id);
}
Если преобразование невозможно, выбрасывается исключение
BadRequestException.
Преобразует строковые значения в булевы:
@Get()
getFlag(@Query('enabled', ParseBoolPipe) enabled: boolean) {
return enabled;
}
Поддерживаются значения true, false,
1, 0.
Используется для преобразования строкового списка в массив:
@Get()
find(@Query('ids', ParseArrayPipe) ids: number[]) {
return ids;
}
Назначает значение по умолчанию:
@Get()
list(@Query('page', new DefaultValuePipe(1)) page: number) {
return page;
}
Один из ключевых pipes в экосистеме. Используется для валидации DTO на основе декораторов.
Пример DTO:
import { IsString, IsInt } fr om 'class-validator';
export class CreateUserDto {
@IsString()
name: string;
@IsInt()
age: number;
}
Использование:
@Post()
create(@Body(new ValidationPipe()) dto: CreateUserDto) {
return this.service.create(dto);
}
Часто применяется на уровне приложения:
app.useGlobalPipes(
new ValidationPipe({
whitelist: true,
transform: true,
forbidNonWhitelisted: true,
}),
);
Параметры:
whitelist — удаляет неизвестные поляtransform — автоматически преобразует типыforbidNonWhitelisted — выбрасывает ошибку при лишних
поляхdisableErrorMessages — скрывает детали ошибокPipes могут не только валидировать, но и изменять данные.
Пример кастомного преобразования строки в объект:
@Injectable()
export class ParseJsonPipe implements PipeTransform {
transform(value: string) {
try {
return JSON.parse(value);
} catch {
throw new BadRequestException('Invalid JSON');
}
}
}
Кастомный pipe реализуется через PipeTransform.
Пример проверки диапазона:
@Injectable()
export class RangePipe implements PipeTransform {
constructor(private min: number, private max: number) {}
transform(value: any) {
const num = Number(value);
if (isNaN(num)) {
throw new BadRequestException('Value must be a number');
}
if (num < this.min || num > this.max) {
throw new BadRequestException('Value out of range');
}
return num;
}
}
Использование:
@Get()
getValue(@Query('val', new RangePipe(1, 100)) val: number) {
return val;
}
Nest не поддерживает DI напрямую в параметризованных конструкторах pipes через декораторы параметров, поэтому часто используется фабричный подход:
export const Range = (min: number, max: number) =>
new RangePipe(min, max);
Pipe может возвращать Promise, что позволяет выполнять
асинхронные операции:
@Injectable()
export class AsyncCheckPipe implements PipeTransform {
async transform(value: any) {
const exists = await this.service.exists(value);
if (!exists) {
throw new BadRequestException('Not found');
}
return value;
}
}
Pipes выполняются:
Этот порядок позволяет комбинировать уровни проверки и трансформации.
Pipes могут быть привязаны:
@Param('id', ParseIntPipe) id: number
@Post()
@UsePipes(ValidationPipe)
create(@Body() dto: CreateUserDto) {}
@UsePipes(ValidationPipe)
@Controller('users')
export class UserController {}
app.useGlobalPipes(new ValidationPipe());
Pipes используют механизм исключений NestJS для прерывания выполнения цепочки обработки.
Чаще всего используется:
BadRequestExceptionUnprocessableEntityExceptionПример:
throw new BadRequestException('Invalid value');
ValidationPipe тесно интегрирован с библиотекой
class-validator и class-transformer.
Пример DTO с трансформацией:
import { Type } from 'class-transformer';
import { IsNumber } from 'class-validator';
export class QueryDto {
@Type(() => Number)
@IsNumber()
lim it: number;
}
Pipes выполняются на раннем этапе запроса, поэтому:
Несколько pipes могут применяться последовательно:
@Param('id', ParseIntPipe, RangePipe(1, 100))
id: number
Каждый pipe получает результат предыдущего.
Pipes выполняются до guards и interceptors на уровне аргументов контроллера. Это создаёт строгую цепочку: