001 Exception filters и pipes

Когда с клиента приходят данные в API, эти данные могут быть провалидированы через Pipes

Когда мы отправляем данные на фронт, мы можем провалидировать эти данные через Exception Filters

Сами http-ошибки в несте создаются через HttpException, который под капотом преобразуется в JSON

Для реализации Exception filter:

  • создаём класс, который экстендится от ExceptionFilter
  • Далее нужно класс обвесить декоратором @Catch(), в котором указать, какие ошибки будет обрабатывать фильтр
  • далее будет описываться метод catch(), который принимает саму ошибку и контекст запроса
  • далее в переменную ctx мы помещаем контекст запроса и из этой переменной дополнительно достаём response и request
  • впоследствии мы можем модифицировать полученный response и обогатить его статусом и нужными данными по ошибке

Чтобы применить созданный фильтр, нужно навесить его на нужный роут через декоратор @UseFilters(), куда мы помещаем созданный инстанс нашего фильтра ошибок

Это приведёт к тому, что все ошибки, которые произойдут в роуте, будут попадать в наш фильтр

В несте уже есть набор готовых пайпов

Чтобы навесить на роут пайп, нужно использовать декоратор @UsePipe() и передать в него нужный инстанс пайпа

Так же мы можем валидировать не полностью целые роуты, а ещё пайпить отдельные значения, просто передавая пайп вторым аргументом в декоратор параметра

Конкретно мы имеем несколько пайпов, которые позволяют преобразовать полученный с клиента ответ в нужный нам тип данных:

  • ParseIntPipe - трансформирует полученное значение с клиента в число (на примере полученный id в number)
  • ParseBoolPipe
  • ParseArrayPipe
  • ParseUUIDPipe
  • DefaultValuePipe

Сам ValidationPipe использует внутри себя библиотеки class-validator и class-transformer, которые позволяют перевести объект в класс и валидировать свойства данного класса

Для реализации своего пайпа, нужно будет создать класс, который будет имплементироваться от интерфейса PipeTransform и реализовывать метод transform(), который принимает первым аргументом значение, а вторым метаданные (они хранят данные о месте применения пайпа, его изначальный тип и аргумент декоратора)

Но зачастую нам нужно использовать пайпы или фильтры по всему приложению. Это можно сделать в основной функции запуска приложения неста

002 Реализация ValidationPipe

Установим две зависимости, которые позволят нам проводить валидацию данных

npm i class-transformer class-validator

Далее нам нужно будет взять декораторы из class-validator, которые позволят нам валидировать значение с клиента и задекорировать все поля класса

Во все данные декораторы мы можем поместить опции, которые позволят заменить стандартные ошибки на те, что нужны нам

src > review > create-review.dto.ts

import { IsString, IsNumber, Min, Max } from 'class-validator';
 
export class CreateReviewDto {
	@IsString()
	name: string;
 
	@IsString()
	title: string;
 
	@IsString({ message: 'Описание должно быть строкой' })
	description: string;
 
	@Max(5, { message: 'Рейтинг не может быть больше 5' })
	@Min(1, { message: 'Рейтинг не может быть меньше 1' })
	@IsNumber()
	rating: number;
 
	@IsString()
	productId: string;
}

Далее нам нужно навесить пайп, который будет валидировать приходящие данные относиельной той DTOшки, которая должна прийти по типу в данный роут (тут dto: CreateReviewDto)

src > review > review.controller.ts

И далее для примера напишем в e2e тесте ещё один кейс для ошибки по типу входящих данных

test > review.e2e-spec.ts

it('/review/create (POST) - fail', async () => {
	return request(app.getHttpServer())
		.post('/review/create')
		.send({ ...testDto, rating: 0 })
		.expect(400)
		.then(({ body }: request.Response) => {
			console.log(body);
		});
});

Мы получаем при ошибке тело с ответом заданной нами ошибки, статускод и error