Пошук уроків, статей та іншого контенту
Налаштуєте відображення DTO, властивостей, валідації та прикладів у Swagger-документації.
DTO (Data Transfer Object) описує формат даних, які контролер приймає або повертає. У NestJS DTO зазвичай є класом із властивостями та декораторами валідації:
export class CreateProductDto {
name: string;
price: number;
}Для валідації використовують class-validator, а для генерації Swagger-документації — @nestjs/swagger.
Ці завдання пов’язані, але не є одним і тим самим:
class-validator перевіряє дані під час виконання програми;
@nestjs/swagger описує DTO у згенерованій документації;
Swagger UI дозволяє побачити очікувану структуру запиту та протестувати endpoint.
Встановіть необхідні пакети:
npm install @nestjs/swagger class-validator class-transformerНалаштуйте Swagger у main.ts:
import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import {
DocumentBuilder,
SwaggerModule,
} from '@nestjs/swagger';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(
new ValidationPipe({
whitelist: true,
transform: true,
}),
);
const config = new DocumentBuilder()
.setTitle('Products API')
.setDescription('API для роботи з товарами')
.setVersion('1.0')
.addTag('products')
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('docs', app, document);
await app.listen(3000);
}
bootstrap();Після запуску застосунку Swagger UI буде доступний за адресою:
http://localhost:3000/docsValidationPipeУ прикладі використано такі параметри:
whitelist: true видаляє властивості, яких немає в DTO;
transform: true перетворює вхідні значення відповідно до типів DTO, коли це можливо.
Для перевірки даних DTO все одно потрібно додати декоратори з class-validator.
Розглянемо DTO для створення товару:
import { ApiProperty } from '@nestjs/swagger';
import {
IsInt,
IsNotEmpty,
IsPositive,
IsString,
MaxLength,
MinLength,
} from 'class-validator';
export class CreateProductDto {
@ApiProperty({
description: 'Назва товару',
example: 'Механічна клавіатура',
minLength: 2,
maxLength: 100,
})
@IsString()
@IsNotEmpty()
@MinLength(2)
@MaxLength(100)
name: string;
@ApiProperty({
description: 'Ціна товару в копійках',
example: 459900,
minimum: 1,
})
@IsInt()
@IsPositive()
price: number;
}@ApiProperty() додає властивість до схеми DTO у Swagger.
Найчастіше в ньому вказують:
description — опис властивості;
example — приклад значення;
required — чи є властивість обов’язковою;
type — тип даних, якщо його неможливо визначити автоматично;
enum — список дозволених значень;
minimum, maximum, minLength, maxLength — обмеження для документації.
Декоратори @IsString(), @IsNotEmpty(), @MinLength() та інші виконують валідацію. Параметри валідації не варто сприймати як повну заміну параметрам Swagger: якщо важливо показати обмеження в документації, їх можна явно вказати в @ApiProperty().
Для необов’язкової властивості використовуйте @ApiPropertyOptional() та @IsOptional():
import { ApiPropertyOptional } from '@nestjs/swagger';
import {
IsOptional,
IsString,
MaxLength,
} from 'class-validator';
export class UpdateProductDescriptionDto {
@ApiPropertyOptional({
description: 'Опис товару',
example: 'Клавіатура з механічними перемикачами',
maxLength: 500,
})
@IsOptional()
@IsString()
@MaxLength(500)
description?: string;
}Різниця між декораторами:
@ApiProperty() позначає властивість як обов’язкову;
@ApiPropertyOptional() позначає властивість як необов’язкову;
@IsOptional() дозволяє пропустити значення під час валідації;
знак ? у TypeScript позначає властивість як необов’язкову на рівні типів.
Щоб поведінка TypeScript, Swagger і валідації збігалася, для необов’язкових полів зазвичай використовують усі відповідні позначення.
PartialTypeДля оновлення ресурсу часто потрібно дозволити передавати лише частину властивостей. Для цього використовується PartialType():
import { PartialType } from '@nestjs/swagger';
import { CreateProductDto } from './create-product.dto';
export class UpdateProductDto extends PartialType(CreateProductDto) {}PartialType(CreateProductDto) створює DTO, у якому всі властивості CreateProductDto стають необов’язковими.
У Swagger такий DTO відображатиметься приблизно так:
{
"name": "Механічна клавіатура",
"price": 459900
}При цьому клієнт може передати лише одну властивість:
{
"price": 499900
}Для PartialType потрібно імпортувати функцію саме з @nestjs/swagger, якщо DTO використовується для генерації Swagger-схеми:
import { PartialType } from '@nestjs/swagger';Для властивості з обмеженим набором значень використовуйте enum у Swagger і @IsEnum() для валідації:
import { ApiProperty } from '@nestjs/swagger';
import { IsEnum } from 'class-validator';
export enum ProductStatus {
ACTIVE = 'active',
ARCHIVED = 'archived',
}
export class UpdateProductStatusDto {
@ApiProperty({
description: 'Статус товару',
enum: ProductStatus,
example: ProductStatus.ACTIVE,
})
@IsEnum(ProductStatus)
status: ProductStatus;
}Swagger покаже допустимі значення active і archived, а class-validator відхилить будь-яке інше значення.
NestJS може автоматично використати DTO параметра методу для опису тіла запиту:
import {
Body,
Controller,
Get,
Param,
Patch,
Post,
} from '@nestjs/common';
import {
ApiCreatedResponse,
ApiOkResponse,
ApiOperation,
ApiTags,
} from '@nestjs/swagger';
import { CreateProductDto } from './dto/create-product.dto';
import { UpdateProductDto } from './dto/update-product.dto';
import { Product } from './product.entity';
@ApiTags('products')
@Controller('products')
export class ProductsController {
@Post()
@ApiOperation({ summary: 'Створити товар' })
@ApiCreatedResponse({
description: 'Товар успішно створено',
type: Product,
})
create(@Body() dto: CreateProductDto): Product {
return {
id: 1,
name: dto.name,
price: dto.price,
};
}
@Patch(':id')
@ApiOperation({ summary: 'Оновити товар' })
@ApiOkResponse({
description: 'Товар успішно оновлено',
type: Product,
})
update(
@Param('id') id: string,
@Body() dto: UpdateProductDto,
): Product {
return {
id: Number(id),
name: dto.name ?? 'Механічна клавіатура',
price: dto.price ?? 459900,
};
}
@Get(':id')
@ApiOperation({ summary: 'Отримати товар' })
@ApiOkResponse({
description: 'Товар знайдено',
type: Product,
})
findOne(@Param('id') id: string): Product {
return {
id: Number(id),
name: 'Механічна клавіатура',
price: 459900,
};
}
}Декоратор @ApiTags('products') групує endpoints контролера в Swagger UI.
@ApiOperation() додає короткий опис операції.
@ApiCreatedResponse() та @ApiOkResponse() описують відповідь endpoint. Параметр type вказує DTO або клас, який описує структуру відповіді.
DTO для запиту та відповіді можуть бути різними. Наприклад, клієнту не потрібно повертати всі внутрішні поля сутності:
import { ApiProperty } from '@nestjs/swagger';
export class Product {
@ApiProperty({
example: 1,
description: 'Унікальний ідентифікатор товару',
})
id: number;
@ApiProperty({
example: 'Механічна клавіатура',
description: 'Назва товару',
})
name: string;
@ApiProperty({
example: 459900,
description: 'Ціна в копійках',
})
price: number;
}У production-застосунках краще не використовувати entity безпосередньо як DTO відповіді, якщо вони містять службові або конфіденційні поля. Окремий response DTO дає змогу явно визначити публічний контракт API.
Приклади окремих властивостей задаються через example у @ApiProperty(). Якщо потрібно показати повний приклад тіла запиту, використовуйте @ApiBody():
import { Body, Controller, Post } from '@nestjs/common';
import {
ApiBody,
ApiCreatedResponse,
ApiTags,
} from '@nestjs/swagger';
import { CreateProductDto } from './dto/create-product.dto';
import { Product } from './product.entity';
@ApiTags('products')
@Controller('products')
export class ProductsController {
@Post()
@ApiBody({
type: CreateProductDto,
examples: {
keyboard: {
summary: 'Приклад клавіатури',
value: {
name: 'Механічна клавіатура',
price: 459900,
},
},
mouse: {
summary: 'Приклад миші',
value: {
name: 'Бездротова миша',
price: 129900,
},
},
},
})
@ApiCreatedResponse({
type: Product,
})
create(@Body() dto: CreateProductDto): Product {
return {
id: 1,
name: dto.name,
price: dto.price,
};
}
}examples містить кілька іменованих прикладів. У Swagger UI користувач зможе вибрати потрібний приклад у полі запиту.
Якщо потрібен лише один приклад без кількох варіантів, можна використати example:
@ApiBody({
type: CreateProductDto,
schema: {
example: {
name: 'Механічна клавіатура',
price: 459900,
},
},
})Для масиву простих значень тип потрібно описати явно:
import { ApiProperty } from '@nestjs/swagger';
import { IsArray, IsString } from 'class-validator';
export class CreateOrderDto {
@ApiProperty({
description: 'Ідентифікатори товарів у замовленні',
type: [String],
example: ['product-1', 'product-2'],
})
@IsArray()
@IsString({ each: true })
productIds: string[];
}Параметр type: [String] повідомляє Swagger, що властивість є масивом рядків.
// src/products/dto/create-product.dto.ts
import { ApiProperty } from '@nestjs/swagger';
import {
IsInt,
IsNotEmpty,
IsPositive,
IsString,
MaxLength,
MinLength,
} from 'class-validator';
export class CreateProductDto {
@ApiProperty({
description: 'Назва товару',
example: 'Механічна клавіатура',
minLength: 2,
maxLength: 100,
})
@IsString()
@IsNotEmpty()
@MinLength(2)
@MaxLength(100)
name: string;
@ApiProperty({
description: 'Ціна в копійках',
example: 459900,
minimum: 1,
})
@IsInt()
@IsPositive()
price: number;
}// src/products/dto/update-product.dto.ts
import { PartialType } from '@nestjs/swagger';
import { CreateProductDto } from './create-product.dto';
export class UpdateProductDto extends PartialType(CreateProductDto) {}// src/products/product.entity.ts
import { ApiProperty } from '@nestjs/swagger';
export class Product {
@ApiProperty({ example: 1 })
id: number;
@ApiProperty({ example: 'Механічна клавіатура' })
name: string;
@ApiProperty({ example: 459900 })
price: number;
}// src/products/products.controller.ts
import {
Body,
Controller,
Get,
Param,
Patch,
Post,
} from '@nestjs/common';
import {
ApiCreatedResponse,
ApiOkResponse,
ApiOperation,
ApiTags,
} from '@nestjs/swagger';
import { CreateProductDto } from './dto/create-product.dto';
import { UpdateProductDto } from './dto/update-product.dto';
import { Product } from './product.entity';
@ApiTags('products')
@Controller('products')
export class ProductsController {
@Post()
@ApiOperation({ summary: 'Створити товар' })
@ApiCreatedResponse({ type: Product })
create(@Body() dto: CreateProductDto): Product {
return {
id: 1,
name: dto.name,
price: dto.price,
};
}
@Patch(':id')
@ApiOperation({ summary: 'Оновити товар' })
@ApiOkResponse({ type: Product })
update(
@Param('id') id: string,
@Body() dto: UpdateProductDto,
): Product {
return {
id: Number(id),
name: dto.name ?? 'Механічна клавіатура',
price: dto.price ?? 459900,
};
}
@Get(':id')
@ApiOperation({ summary: 'Отримати товар' })
@ApiOkResponse({ type: Product })
findOne(@Param('id') id: string): Product {
return {
id: Number(id),
name: 'Механічна клавіатура',
price: 459900,
};
}
}Після відкриття Swagger UI endpoint POST /products матиме:
опис операції;
схему CreateProductDto;
обов’язкові властивості name і price;
типи та приклади властивостей;
обмеження довжини назви та мінімальну ціну;
модель відповіді Product.
Наявність лише декораторів class-validator не гарантує потрібного опису в документації. Для важливих властивостей додавайте @ApiProperty() або @ApiPropertyOptional().
@ApiProperty({ example: 'Механічна клавіатура' })
@IsString()
name: string;Якщо властивість може бути відсутньою, використовуйте:
@ApiPropertyOptional()
@IsOptional()
description?: string;А для DTO оновлення застосовуйте PartialType().
Декоратори class-validator працюють лише разом із ValidationPipe:
app.useGlobalPipes(new ValidationPipe());Без цього DTO описуватиме структуру даних, але запити не перевірятимуться автоматично.
Для масивів явно вказуйте тип:
@ApiProperty({ type: [String] })
tags: string[];Значення в example має відповідати реальним правилам DTO. Якщо price має бути додатним цілим числом, приклад -10 або 12.5 вводитиме користувача в оману.
PartialType імпортовано не з того пакетаДля DTO, які мають відображатися у Swagger, використовуйте:
import { PartialType } from '@nestjs/swagger';@ApiProperty() описує обов’язкову властивість DTO у Swagger.
@ApiPropertyOptional() описує необов’язкову властивість.
Декоратори class-validator відповідають за перевірку даних під час виконання.
ValidationPipe потрібно підключити глобально або на рівні конкретного контролера.
PartialType() зручно використовувати для DTO часткового оновлення.
@ApiOperation(), @ApiTags() і response-декоратори роблять endpoints зрозумілими в Swagger UI.
example описує приклад значення властивості, а @ApiBody() дає змогу показати повний приклад запиту.
DTO відповіді допомагає явно визначити дані, які API повертає клієнту.