Пошук уроків, статей та іншого контенту
Використаєте вбудовані ParseIntPipe, ParseBoolPipe, ParseUUIDPipe та інші pipes для перетворення й перевірки параметрів.
Pipe у NestJS — це клас, який обробляє значення перед передаванням його в метод контролера. Вбудовані pipes найчастіше використовують для:
перетворення рядків із URL або query-параметрів у потрібний тип;
перевірки формату значення;
встановлення значення за замовчуванням;
відхилення некоректного запиту з помилкою 400 Bad Request.
Параметри HTTP-запиту спочатку надходять у застосунок як рядки. Наприклад, значення id із маршруту /users/42 буде рядком "42", навіть якщо в TypeScript параметр оголошений як number.
@Get(':id')
findOne(@Param('id') id: number) {
// Без pipe тут фактично буде string
return this.usersService.findOne(id);
}Анотація : number впливає лише на перевірку типів під час компіляції. Вона не виконує перетворення під час роботи програми.
Для перетворення та перевірки потрібно застосувати pipe.
ParseIntPipe перетворює значення на ціле число. Якщо значення не можна коректно перетворити, NestJS автоматично повертає помилку 400 Bad Request.
import { Controller, Get, Param, ParseIntPipe } from '@nestjs/common';
@Controller('users')
export class UsersController {
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {
return {
id,
type: typeof id,
};
}
}Запит:
GET /users/42Результат:
{
"id": 42,
"type": "number"
}Запит із некоректним значенням:
GET /users/abcзавершиться помилкою 400 Bad Request.
Pipe можна створити через new, якщо потрібно передати параметри конфігурації:
@Param('id', new ParseIntPipe({ errorHttpStatusCode: 422 }))
id: numberУ цьому випадку помилка перетворення матиме статус 422, а не стандартний 400.
ParseFloatPipe працює подібно до ParseIntPipe, але перетворює значення на число з плаваючою крапкою.
import {
Controller,
Get,
ParseFloatPipe,
Query,
} from '@nestjs/common';
@Controller('products')
export class ProductsController {
@Get()
findByPrice(
@Query('minPrice', ParseFloatPipe) minPrice: number,
) {
return {
minPrice,
type: typeof minPrice,
};
}
}Запит:
GET /products?minPrice=19.99Параметр minPrice у методі контролера матиме тип number і значення 19.99.
ParseBoolPipe перетворює рядкові значення "true" і "false" на логічні true та false.
import {
Controller,
Get,
ParseBoolPipe,
Query,
} from '@nestjs/common';
@Controller('products')
export class ProductsController {
@Get()
findAll(
@Query('available', ParseBoolPipe) available: boolean,
) {
return {
available,
type: typeof available,
};
}
}Запит:
GET /products?available=trueРезультат:
{
"available": true,
"type": "boolean"
}Значення на кшталт "1", "0", "yes" або "no" не є стандартними значеннями для цього pipe і будуть відхилені.
ParseUUIDPipe перевіряє, чи є значення коректним UUID. За замовчуванням перевіряються UUID підтримуваних версій.
import {
Controller,
Get,
Param,
ParseUUIDPipe,
} from '@nestjs/common';
@Controller('orders')
export class OrdersController {
@Get(':id')
findOne(
@Param('id', ParseUUIDPipe) id: string,
) {
return {
id,
message: 'Замовлення знайдено',
};
}
}Коректний запит:
GET /orders/550e8400-e29b-41d4-a716-446655440000Можна обмежити перевірку конкретною версією UUID:
@Param('id', new ParseUUIDPipe({ version: '4' }))
id: stringЦе корисно, коли API очікує UUID певної версії.
ParseEnumPipe перевіряє, чи належить значення одному з елементів enum.
import {
Controller,
Get,
ParseEnumPipe,
Query,
} from '@nestjs/common';
enum ProductSort {
Price = 'price',
Name = 'name',
CreatedAt = 'createdAt',
}
@Controller('products')
export class ProductsController {
@Get()
findAll(
@Query('sort', new ParseEnumPipe(ProductSort))
sort: ProductSort,
) {
return {
sort,
};
}
}Коректний запит:
GET /products?sort=priceНекоректний запит:
GET /products?sort=ratingУ другому випадку NestJS поверне 400 Bad Request, оскільки rating відсутній у ProductSort.
DefaultValuePipe встановлює значення, якщо параметр не передали.
Найчастіше його комбінують із pipe перетворення типу:
import {
Controller,
DefaultValuePipe,
Get,
ParseIntPipe,
Query,
} from '@nestjs/common';
@Controller('products')
export class ProductsController {
@Get()
findAll(
@Query('page', new DefaultValuePipe(1), ParseIntPipe)
page: number,
) {
return {
page,
};
}
}Запит без параметра:
GET /productsдасть такий результат:
{
"page": 1
}Запит:
GET /products?page=3дасть:
{
"page": 3
}Тут використовуються два pipes:
DefaultValuePipe(1) встановлює значення 1, якщо page відсутній.
ParseIntPipe перетворює значення на число.
Порядок важливий: спочатку потрібно забезпечити значення за замовчуванням, а потім перетворити його.
ParseArrayPipe перетворює параметр на масив і, за потреби, перетворює його елементи в заданий тип.
Наприклад, API може приймати список ідентифікаторів:
import {
Controller,
Get,
ParseArrayPipe,
Query,
} from '@nestjs/common';
@Controller('products')
export class ProductsController {
@Get()
findMany(
@Query(
'ids',
new ParseArrayPipe({ items: Number }),
)
ids: number[],
) {
return {
ids,
typeOfFirstItem: typeof ids[0],
};
}
}Запит:
GET /products?ids=10,20,30Результат:
{
"ids": [10, 20, 30],
"typeOfFirstItem": "number"
}За замовчуванням елементи рядкового параметра розділяються комою. Роздільник можна змінити:
new ParseArrayPipe({
items: String,
separator: '|',
})Тоді запит із tags=nestjs|typescript|api буде перетворено на масив:
["nestjs", "typescript", "api"]Властивість items визначає конструктор, який використовують для перетворення елементів, наприклад Number або String.
Для одного параметра можна вказати кілька pipes:
@Get()
findAll(
@Query(
'page',
new DefaultValuePipe(1),
ParseIntPipe,
)
page: number,
) {
return { page };
}Також pipes можна застосовувати до параметрів маршруту та query-параметрів одночасно:
import {
Controller,
Get,
Param,
ParseBoolPipe,
ParseIntPipe,
Query,
} from '@nestjs/common';
@Controller('reports')
export class ReportsController {
@Get(':reportId')
getReport(
@Param('reportId', ParseIntPipe) reportId: number,
@Query('includeDetails', ParseBoolPipe) includeDetails: boolean,
) {
return {
reportId,
includeDetails,
};
}
}Запит:
GET /reports/15?includeDetails=trueУ метод передадуться:
reportId === 15;
includeDetails === true;Нижче наведено контролер, який можна додати до звичайного NestJS-проєкту.
import {
Controller,
DefaultValuePipe,
Get,
Param,
ParseArrayPipe,
ParseBoolPipe,
ParseEnumPipe,
ParseIntPipe,
ParseUUIDPipe,
Query,
} from '@nestjs/common';
enum OrderStatus {
Pending = 'pending',
Paid = 'paid',
Cancelled = 'cancelled',
}
@Controller('orders')
export class OrdersController {
@Get()
findAll(
@Query('page', new DefaultValuePipe(1), ParseIntPipe)
page: number,
@Query('includeCancelled', ParseBoolPipe)
includeCancelled: boolean,
@Query(
'ids',
new ParseArrayPipe({ items: Number }),
)
ids: number[],
@Query('status', new ParseEnumPipe(OrderStatus))
status: OrderStatus,
) {
return {
page,
includeCancelled,
ids,
status,
};
}
@Get(':id')
findOne(
@Param('id', ParseUUIDPipe)
id: string,
) {
return {
id,
};
}
}Приклад запиту:
GET /orders?page=2&includeCancelled=false&ids=10,20&status=paidМетод findAll отримає вже оброблені значення:
{
page: 2,
includeCancelled: false,
ids: [10, 20],
status: OrderStatus.Paid
}Pipe за замовчуванням очікує, що параметр буде переданий. Якщо query-параметр необов’язковий, для нього можна використати DefaultValuePipe.
@Get()
findAll(
@Query('page', new DefaultValuePipe(1), ParseIntPipe)
page: number,
@Query(
'includeDetails',
new DefaultValuePipe(false),
ParseBoolPipe,
)
includeDetails: boolean,
) {
return {
page,
includeDetails,
};
}Тепер запит /orders буде еквівалентним запиту:
/orders?page=1&includeDetails=falseЯкщо значення не проходить перевірку, вбудований pipe викидає HTTP-помилку. Наприклад:
ParseIntPipe відхиляє значення, яке не є цілим числом;
ParseFloatPipe відхиляє значення, яке не є числом;
ParseBoolPipe відхиляє значення, відмінне від true або false;
ParseUUIDPipe відхиляє некоректний UUID;
ParseEnumPipe відхиляє значення, якого немає в enum;
ParseArrayPipe відхиляє значення, яке не можна перетворити на очікуваний масив.
Це означає, що в методі контролера можна працювати з уже перевіреними даними, не дублюючи прості перевірки вручну.
@Get()
find(@Query('page') page: number) {
// page усе ще є рядком
}TypeScript-тип number не перетворює значення під час виконання. Потрібно використати ParseIntPipe.
?active=1ParseBoolPipe очікує true або false, а не 1 або 0.
@Query('page', ParseIntPipe, new DefaultValuePipe(1))
page: numberЯкщо параметр відсутній, ParseIntPipe може отримати undefined раніше, ніж буде встановлено значення за замовчуванням. Використовуйте такий порядок:
@Query('page', new DefaultValuePipe(1), ParseIntPipe)
page: numberЯкщо застосовано ParseEnumPipe, клієнт повинен передати допустиме значення:
@Query('status', new ParseEnumPipe(OrderStatus))
status: OrderStatusЗапит без status буде відхилено, якщо не встановлено значення за замовчуванням.
ParseUUIDPipe перевіряє формат UUID, але не перетворює його на інший тип. Результат залишається рядком:
@Param('id', ParseUUIDPipe)
id: stringЯкщо ParseArrayPipe використовує роздільник за замовчуванням, список потрібно передавати через кому:
?ids=1,2,3Не слід очікувати автоматичного оброблення довільного формату без налаштування separator.
ParseIntPipe перетворює параметр на ціле число.
ParseFloatPipe перетворює параметр на число з плаваючою крапкою.
ParseBoolPipe перетворює "true" і "false" на boolean.
ParseUUIDPipe перевіряє формат UUID.
ParseEnumPipe обмежує значення елементами enum.
ParseArrayPipe перетворює параметр на масив і може перетворювати його елементи.
DefaultValuePipe задає значення для відсутнього параметра.
Некоректні значення автоматично призводять до HTTP-помилки.
Типи TypeScript самі по собі не перетворюють значення HTTP-запиту.
Кілька pipes можна комбінувати, а їхній порядок має значення.