Пошук уроків, статей та іншого контенту
Навчитеся отримувати параметри маршруту й query-параметри та типізувати їх у контролерах NestJS.
Параметри маршруту — це змінні частини URL. Вони позначаються двокрапкою в декораторі маршруту.
Наприклад, у маршруті:
/users/:idчастина :id є параметром маршруту. Для URL /users/42 значенням параметра id буде "42".
У NestJS параметри маршруту отримують за допомогою декоратора @Param():
import { Controller, Get, Param } from '@nestjs/common';
@Controller('users')
export class UsersController {
@Get(':id')
findOne(@Param('id') id: string) {
return {
id,
message: `Користувача з ідентифікатором ${id} знайдено`,
};
}
}Для запиту:
GET /users/42метод отримає таке значення:
id === '42'Якщо маршрут має кілька параметрів, їх можна отримати одним викликом @Param():
@Get('posts/:postId/comments/:commentId')
findComment(@Param() params: { postId: string; commentId: string }) {
return {
postId: params.postId,
commentId: params.commentId,
};
}Для запиту:
GET /users/posts/10/comments/25об’єкт params матиме вигляд:
{
postId: '10',
commentId: '25'
}Тип { postId: string; commentId: string } допомагає TypeScript перевіряти код під час розробки.
Query-параметри розташовані після знака ? у URL.
Наприклад:
/users?role=admin&limit=10У цьому URL є два query-параметри:
role зі значенням admin;
limit зі значенням 10.
Для отримання query-параметра використовують @Query():
import { Controller, Get, Query } from '@nestjs/common';
@Controller('users')
export class UsersController {
@Get()
findAll(
@Query('role') role?: string,
@Query('limit') limit?: string,
) {
return {
role,
limit,
};
}
}Для запиту:
GET /users?role=admin&limit=10результат буде таким:
{
"role": "admin",
"limit": "10"
}HTTP передає значення параметрів як текст. Навіть якщо в URL написано limit=10, NestJS за замовчуванням отримає рядок "10", а не число 10.
Анотація TypeScript не перетворює значення:
@Query('limit') limit: numberТакий запис повідомляє TypeScript про очікуваний тип, але фактичне значення все одно може бути рядком.
Для перетворення рядка на число можна використати вбудований ParseIntPipe.
ParseIntPipe перетворює значення на ціле число. Якщо значення не можна перетворити, NestJS автоматично поверне помилку HTTP 400 Bad Request.
import {
Controller,
DefaultValuePipe,
Get,
Param,
ParseIntPipe,
Query,
} from '@nestjs/common';
@Controller('users')
export class UsersController {
@Get()
findAll(
@Query('role') role: string | undefined,
@Query('limit', new DefaultValuePipe(10), ParseIntPipe) limit: number,
) {
return {
role: role ?? null,
limit,
limitType: typeof limit,
};
}
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {
return {
id,
idType: typeof id,
};
}
}Тепер:
GET /users?role=admin&limit=20поверне:
{
"role": "admin",
"limit": 20,
"limitType": "number"
}А запит:
GET /users/42поверне:
{
"id": 42,
"idType": "number"
}Якщо виконати запит:
GET /users/abcParseIntPipe не зможе перетворити abc на число, тому NestJS поверне відповідь зі статусом 400.
У прикладі використано DefaultValuePipe(10):
@Query('limit', new DefaultValuePipe(10), ParseIntPipe)
limit: numberЯкщо параметр limit відсутній:
GET /usersNestJS спочатку використає значення 10, а потім перетворить його на число.
Результат:
{
"role": null,
"limit": 10,
"limitType": "number"
}Нижче наведено контролер, який підтримує параметр маршруту id, а також query-параметри role, limit і active.
import {
Controller,
DefaultValuePipe,
Get,
Param,
ParseBoolPipe,
ParseIntPipe,
Query,
} from '@nestjs/common';
@Controller('users')
export class UsersController {
@Get()
findAll(
@Query('role') role: string | undefined,
@Query('limit', new DefaultValuePipe(10), ParseIntPipe) limit: number,
@Query('active', new DefaultValuePipe(true), ParseBoolPipe) active: boolean,
) {
return {
filters: {
role: role ?? null,
limit,
active,
},
};
}
@Get(':id')
findOne(
@Param('id', ParseIntPipe) id: number,
@Query('details') details: string | undefined,
) {
return {
id,
details: details ?? null,
};
}
}Приклади запитів:
GET /users{
"filters": {
"role": null,
"limit": 10,
"active": true
}
}GET /users?role=admin&limit=5&active=false{
"filters": {
"role": "admin",
"limit": 5,
"active": false
}
}GET /users/15?details=full{
"id": 15,
"details": "full"
}Параметри маршруту є частиною структури URL:
/users/15Тут 15 — параметр маршруту id.
Query-параметри записуються після ? і зазвичай використовуються для фільтрації, сортування або налаштування відповіді:
/users?role=admin&limit=10Тут role і limit — query-параметри.
У контролері вони отримуються різними декораторами:
@Get(':id')
findOne(
@Param('id', ParseIntPipe) id: number,
@Query('details') details?: string,
) {
return { id, details };
}Коли query-параметрів стає багато, їх можна отримати одним об’єктом і описати його тип.
import { Controller, Get, Query } from '@nestjs/common';
type UsersQuery = {
role?: string;
sort?: string;
limit?: string;
};
@Controller('users')
export class UsersController {
@Get()
findAll(@Query() query: UsersQuery) {
return {
role: query.role ?? null,
sort: query.sort ?? null,
limit: query.limit ?? null,
};
}
}Для URL:
/users?role=admin&sort=name&limit=20об’єкт query матиме такий вигляд:
{
role: 'admin',
sort: 'name',
limit: '20'
}Зверніть увагу: у типі UsersQuery параметр limit має тип string, оскільки query-параметри надходять як рядки.
Якщо потрібно отримати limit саме як число, краще використати ParseIntPipe безпосередньо в параметрі методу:
@Get()
findAll(
@Query('role') role: string | undefined,
@Query('limit', new DefaultValuePipe(10), ParseIntPipe) limit: number,
) {
return { role, limit };
}Контролер потрібно додати до модуля:
import { Module } from '@nestjs/common';
import { UsersController } from './users.controller';
@Module({
controllers: [UsersController],
})
export class AppModule {}Після запуску застосунку можна виконати запити:
GET /users/7
GET /users?role=admin
GET /users?limit=25
GET /users/7?details=fullПомилкове припущення:
@Get(':id')
findOne(@Param('id') id: number) {
return id + 1;
}Фактично id може бути рядком "10", тому результатом буде "101", а не 11.
Правильний варіант:
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {
return id + 1;
}Якщо маршрут оголошено так:
@Get(':userId')
findOne(@Param('id') id: string) {
return id;
}параметр id не існує. Ім’я в @Param() має збігатися з іменем у маршруті:
@Get(':userId')
findOne(@Param('userId') userId: string) {
return userId;
}Query-параметр може бути відсутнім:
GET /usersТому для нього варто використовувати тип string | undefined або оператор значення за замовчуванням:
@Get()
findAll(@Query('role') role?: string) {
return {
role: role ?? 'all',
};
}Якщо метод очікує число:
@Get()
findAll(
@Query('limit', ParseIntPipe) limit: number,
) {
return { limit };
}то запит із limit=abc завершиться помилкою 400. Це очікувана поведінка: pipe захищає контролер від некоректних даних.
Параметри маршруту описують змінні частини URL, наприклад :id.
Для отримання параметрів маршруту використовують @Param().
Query-параметри розташовані після ?.
Для отримання query-параметрів використовують @Query().
Значення параметрів за замовчуванням надходять як рядки.
TypeScript-тип не перетворює значення під час виконання.
Для перетворення рядка на число використовують ParseIntPipe.
Для перетворення рядка на boolean використовують ParseBoolPipe.
DefaultValuePipe дає параметру значення, якщо його не передали.