Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Пайпы

Пайп (pipe) — класс, через который проходит аргумент перед тем, как попасть в метод контроллера. Пайп может преобразовать значение (строку "5" в число 5) или проверить его и отклонить запрос с ошибкой 400. ValidationPipe из прошлого урока — тоже пайп.

Встроенный ParseIntPipe

Параметры адреса приходят строками. ParseIntPipe превращает строку в целое число:

// src/posts/posts.controller.ts
import { Controller, Get, Param, ParseIntPipe } from '@nestjs/common';
import { PostsService } from './posts.service';
 
@Controller('posts')
export class PostsController {
  constructor(private readonly postsService: PostsService) {}
 
  @Get(':id')
  findOne(@Param('id', ParseIntPipe) id: number) {
    return this.postsService.findOne(id);
  }
}

Пайп передаётся вторым аргументом декоратора. Теперь id — настоящее число, и Number(id) больше не нужен. Откройте /posts/abc — вместо пустого ответа придёт ошибка 400 с текстом Validation failed (numeric string is expected).

Значение по умолчанию

DefaultValuePipe подставляет значение, если параметра нет. Пайпы выполняются слева направо:

// src/posts/posts.controller.ts
import { DefaultValuePipe, Get, ParseIntPipe, Query } from '@nestjs/common';
 
@Get()
findAll(@Query('page', new DefaultValuePipe(1), ParseIntPipe) page: number) {
  return { page };
}

/posts вернёт {"page":1}, а /posts?page=3 — {"page":3}.

Встроенные пайпы

ПайпЧто делает
ParseIntPipeСтроку в целое число
ParseFloatPipeСтроку в дробное число
ParseBoolPipe"true" и "false" в логическое значение
ParseUUIDPipeПроверяет, что строка — UUID
ParseEnumPipeПроверяет, что значение из перечисления
DefaultValuePipeПодставляет значение по умолчанию
ValidationPipeПроверяет объект по правилам DTO

Свой пайп

Номер страницы не может быть нулём или отрицательным. Создайте пайп:

nest g pipe common/pipes/positive-int --flat
// src/common/pipes/positive-int.pipe.ts
import { BadRequestException, Injectable, PipeTransform } from '@nestjs/common';
 
@Injectable()
export class PositiveIntPipe implements PipeTransform {
  transform(value: string): number {
    const number = Number(value);
 
    if (!Number.isInteger(number) || number < 1) {
      throw new BadRequestException('Ожидается целое число больше нуля');
    }
 
    return number;
  }
}

Пайп реализует интерфейс PipeTransform: метод transform() получает значение и возвращает то, что попадёт в аргумент. Брошенное исключение BadRequestException превращается в ответ 400.

Пайп подключают так же, как встроенные:

// src/posts/posts.controller.ts
import { PositiveIntPipe } from '../common/pipes/positive-int.pipe';
 
@Get(':id')
findOne(@Param('id', PositiveIntPipe) id: number) {
  return this.postsService.findOne(id);
}

Адрес /posts/0 вернёт 400 с сообщением «Ожидается целое число больше нуля».

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

Без пайпа тип number у аргумента ничего не меняет: TypeScript не преобразует данные во время работы, и в id по-прежнему лежит строка. Сравнение post.id === id тогда никогда не совпадёт, и сервис вернёт пустой ответ. Если аргумент объявлен числом, к нему нужен ParseIntPipe или transform: true в ValidationPipe.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.