Kurs NestJS · Moduł 11: Swagger i OpenAPI
Dokumentacja DTO - pieczęcie na pergaminie
W tej lekcji7
Każdy dokument w cesarskiej kancelarii musiał mieć precyzyjnie opisane pola - imię petenta, rodzaj sprawy, datę i pieczęć. W NestJS odpowiednikiem tych dokumentów są DTO (Data Transfer Objects), a dekorator @ApiProperty() opisuje każde pole tak, aby Swagger UI mógł je wyświetlić.
@ApiProperty - Podstawy
Dekorator @ApiProperty() opisuje pojedyncze pole w klasie DTO:
1import { ApiProperty } from '@nestjs/swagger';
2
3export class CreateLegionaryDto {
4 @ApiProperty({
5 description: 'Imię legionisty',
6 example: 'Marcus Aurelius',
7 })
8 name: string;
9
10 @ApiProperty({
11 description: 'Ranga w legionie',
12 example: 'Centurio',
13 })
14 rank: string;
15
16 @ApiProperty({
17 description: 'Lata doświadczenia',
18 example: 5,
19 minimum: 0,
20 maximum: 40,
21 })
22 experience: number;
23}Opcje @ApiProperty
Dekorator przyjmuje wiele opcji konfiguracyjnych:
| Opcja | Opis | Przykład |
|---|---|---|
description | Opis pola | 'Imię legionisty' |
example | Przykładowa wartość | 'Marcus' |
required | Czy pole jest wymagane | true / false |
default | Wartość domyślna | 'Miles' |
enum | Lista dozwolonych wartości | ['Centurio', 'Miles'] |
type | Typ pola | String, Number, Boolean |
isArray | Czy pole jest tablicą | true |
minimum | Minimalna wartość (number) | 0 |
maximum | Maksymalna wartość (number) | 100 |
minLength | Minimalna długość (string) | 2 |
maxLength | Maksymalna długość (string) | 50 |
Pola opcjonalne z @ApiPropertyOptional
Dla pól opcjonalnych używamy @ApiPropertyOptional():
1import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
2
3export class UpdateLegionaryDto {
4 @ApiPropertyOptional({
5 description: 'Nowa ranga legionisty',
6 example: 'Optio',
7 })
8 rank?: string;
9
10 @ApiPropertyOptional({
11 description: 'Nowy legion przypisania',
12 example: 'Legio III Augusta',
13 })
14 legio?: string;
15
16 @ApiPropertyOptional({
17 description: 'Dodatkowe doświadczenie',
18 example: 2,
19 minimum: 0,
20 })
21 additionalExperience?: number;
22}Pola z enum - rangi legionowe
Enum jest szczególnie przydatny do pól z ograniczonym zestawem wartości:
1export enum LegionaryRank {
2 MILES = 'Miles',
3 OPTIO = 'Optio',
4 CENTURIO = 'Centurio',
5 TRIBUNUS = 'Tribunus',
6 LEGATUS = 'Legatus',
7}
8
9export class CreateLegionaryDto {
10 @ApiProperty({
11 description: 'Ranga legionisty',
12 enum: LegionaryRank,
13 example: LegionaryRank.MILES,
14 })
15 rank: LegionaryRank;
16}Zagnieżdżone obiekty - struktura legionu
Dla zagnieżdżonych DTO używamy opcji type:
1export class AddressDto {
2 @ApiProperty({ description: 'Nazwa prowincji', example: 'Pannonia' })
3 province: string;
4
5 @ApiProperty({ description: 'Nazwa miasta', example: 'Aquincum' })
6 city: string;
7}
8
9export class LegionDto {
10 @ApiProperty({ description: 'Nazwa legionu', example: 'Legio X Gemina' })
11 name: string;
12
13 @ApiProperty({
14 description: 'Lokalizacja legionu',
15 type: AddressDto,
16 })
17 location: AddressDto;
18
19 @ApiProperty({
20 description: 'Lista centurionów',
21 type: [String],
22 example: ['Marcus', 'Julius', 'Gaius'],
23 })
24 centurions: string[];
25}DTO odpowiedzi
Warto tworzyć osobne DTO dla odpowiedzi API:
1export class LegionaryResponseDto {
2 @ApiProperty({ description: 'ID legionisty', example: '507f1f77bcf86cd799439011' })
3 id: string;
4
5 @ApiProperty({ description: 'Imię legionisty', example: 'Marcus Aurelius' })
6 name: string;
7
8 @ApiProperty({ description: 'Ranga', enum: LegionaryRank })
9 rank: LegionaryRank;
10
11 @ApiProperty({ description: 'Data rekrutacji', example: '2024-01-15T10:30:00Z' })
12 recruitedAt: Date;
13
14 @ApiProperty({ description: 'Czy aktywny', example: true })
15 isActive: boolean;
16}Ćwiczenie praktyczne
Stwórz dokumentację DTO dla systemu zarządzania prowincjami Imperium:
Kod do tej lekcji: src/province.dto.ts
1// Dokumentacja DTO - pieczecie na pergaminie
2import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
3
4// TODO: Stworz enum ProvinceStatus z wartosciami:
5// ACTIVE = 'Active', CONQUERED = 'Conquered', REBELLING = 'Rebelling'
6export enum ProvinceStatus {
7 // TODO: Uzupelnij wartosci enum
8}
9
10export class CreateProvinceDto {
11 // TODO: Dodaj @ApiProperty z description i example
12 name: string;
13
14 // TODO: Dodaj @ApiProperty z description i example
15 governor: string;
16
17 // TODO: Dodaj @ApiProperty z enum ProvinceStatus
18 status: ProvinceStatus;
19
20 // TODO: Dodaj @ApiPropertyOptional z description, example, minimum
21 population?: number;
22
23 // TODO: Dodaj @ApiProperty z description i type [String]
24 legions: string[];
25}
26
27export class ProvinceResponseDto {
28 @ApiProperty({ description: 'ID prowincji', example: '64a1b2c3d4e5f6a7b8c9d0e1' })
29 id: string;
30
31 @ApiProperty({ description: 'Nazwa prowincji', example: 'Pannonia' })
32 name: string;
33
34 @ApiProperty({ description: 'Namiestnik', example: 'Lucius Verus' })
35 governor: string;
36
37 @ApiProperty({ description: 'Status prowincji', enum: ProvinceStatus })
38 status: ProvinceStatus;
39
40 @ApiPropertyOptional({ description: 'Populacja', example: 500000 })
41 population?: number;
42
43 @ApiProperty({ description: 'Data aneksji' })
44 annexedAt: Date;
45}
46
47console.log("@ApiProperty - wymagane pola");
48console.log("@ApiPropertyOptional - opcjonalne pola");
49console.log("Opcje: description, example, enum, type");
50console.log("minimum, maximum, minLength, maxLength");
51Widzisz błąd w tej lekcji?
Sprawdź się
Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.
1. Co robi dekorator @ApiProperty() na polu klasy DTO?
2. Jaki dekorator służy do oznaczania pól opcjonalnych w DTO dla Swagger?
To 2 z 3 pytań do tej lekcji. Pozostałe rozwiążesz w grze.
Zadania praktyczne w grze
- Edytor kodu
Uzupełnij CreateCenturioDto dodając @ApiProperty z description i example do każdego pola
- Układanie w pionie
Ułóż elementy @ApiProperty z enum w prawidłowej kolejności:
- Klikanie w kolejności
Ułóż opcje @ApiProperty od opisu do walidacji wartości:
- Edytor kodu
Uzupełnij CreateProvinceDto z enum ProvinceType, zagnieżdżonym LocationDto i tablicą stringów