Kurs NestJS · Moduł 11: Swagger i OpenAPI
Projekt główny: Kompletna dokumentacja Imperium API
W tej lekcji3
Nadszedł czas na wielkie dzieło - stworzenie kompletnych Kronik Imperium! W tym projekcie połączysz całą wiedzę z modułu, aby stworzyć w pełni udokumentowane API zarządzania Imperium Rzymskim.
Wymagania projektu
Twoje Kroniki Imperium muszą zawierać:
1. Konfiguracja Swagger w main.ts
1import { NestFactory } from '@nestjs/core';
2import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
3import { ValidationPipe } from '@nestjs/common';
4import { AppModule } from './app.module';
5
6async function bootstrap() {
7 const app = await NestFactory.create(AppModule);
8
9 app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true }));
10
11 const config = new DocumentBuilder()
12 .setTitle('Imperium Romanum API')
13 .setDescription('Kompletna dokumentacja API zarządzania Imperium Rzymskim')
14 .setVersion('1.0')
15 .addTag('Legiones', 'Zarządzanie legionami i żołnierzami')
16 .addTag('Provinciae', 'Zarządzanie prowincjami Imperium')
17 .addTag('Tributum', 'System podatkowy i skarbiec')
18 .addBearerAuth(
19 { type: 'http', scheme: 'bearer', bearerFormat: 'JWT' },
20 'JWT-auth',
21 )
22 .setContact('Konsul Maximus', 'https://imperium.rome', 'consul@rome.gov')
23 .setLicense('Lex Romana', 'https://imperium.rome/lex')
24 .build();
25
26 const document = SwaggerModule.createDocument(app, config);
27
28 SwaggerModule.setup('api/docs', app, document, {
29 customSiteTitle: 'Kroniki Imperium API',
30 swaggerOptions: {
31 persistAuthorization: true,
32 filter: true,
33 showRequestDuration: true,
34 },
35 });
36
37 await app.listen(3000);
38}
39bootstrap();2. DTO z pełną dokumentacją
1import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
2
3export enum LegionaryRank {
4 MILES = 'Miles',
5 OPTIO = 'Optio',
6 CENTURIO = 'Centurio',
7 TRIBUNUS = 'Tribunus',
8 LEGATUS = 'Legatus',
9}
10
11export class CreateLegionaryDto {
12 @ApiProperty({ description: 'Imię legionisty', example: 'Marcus Aurelius', minLength: 2 })
13 name: string;
14
15 @ApiProperty({ description: 'Ranga', enum: LegionaryRank, example: LegionaryRank.MILES })
16 rank: LegionaryRank;
17
18 @ApiProperty({ description: 'Przypisany legion', example: 'Legio X Gemina' })
19 legio: string;
20
21 @ApiPropertyOptional({ description: 'Lata doświadczenia', example: 3, minimum: 0, maximum: 40 })
22 experience?: number;
23}
24
25export class LegionaryResponseDto {
26 @ApiProperty({ description: 'ID legionisty', example: '64a1b2c3d4e5f6a7b8c9d0e1' })
27 id: string;
28
29 @ApiProperty({ description: 'Imię', example: 'Marcus Aurelius' })
30 name: string;
31
32 @ApiProperty({ description: 'Ranga', enum: LegionaryRank })
33 rank: LegionaryRank;
34
35 @ApiProperty({ description: 'Legion', example: 'Legio X Gemina' })
36 legio: string;
37
38 @ApiProperty({ description: 'Doświadczenie', example: 5 })
39 experience: number;
40
41 @ApiProperty({ description: 'Data rekrutacji' })
42 recruitedAt: Date;
43}3. Kontroler z pełną dokumentacją
1import { Controller, Get, Post, Put, Delete, Param, Body, Query, UseGuards } from '@nestjs/common';
2import { ApiTags, ApiOperation, ApiParam, ApiQuery, ApiBearerAuth,
3 ApiOkResponse, ApiCreatedResponse, ApiNotFoundResponse,
4 ApiBadRequestResponse, ApiUnauthorizedResponse } from '@nestjs/swagger';
5
6@ApiTags('Legiones')
7@Controller('legiones')
8export class LegionController {
9 @ApiOperation({ summary: 'Pobierz wszystkie legiony' })
10 @ApiOkResponse({ description: 'Lista legionów', type: [LegionaryResponseDto] })
11 @ApiQuery({ name: 'rank', required: false, enum: LegionaryRank })
12 @Get()
13 findAll(@Query('rank') rank?: LegionaryRank) {}
14
15 @ApiOperation({ summary: 'Pobierz legionistę po ID' })
16 @ApiParam({ name: 'id', description: 'ID legionisty' })
17 @ApiOkResponse({ type: LegionaryResponseDto })
18 @ApiNotFoundResponse({ description: 'Legionista nie znaleziony' })
19 @Get(':id')
20 findOne(@Param('id') id: string) {}
21
22 @ApiBearerAuth('JWT-auth')
23 @ApiOperation({ summary: 'Rekrutuj legionistę' })
24 @ApiCreatedResponse({ type: LegionaryResponseDto })
25 @ApiBadRequestResponse({ description: 'Nieprawidłowe dane' })
26 @ApiUnauthorizedResponse({ description: 'Brak autoryzacji' })
27 @Post()
28 create(@Body() dto: CreateLegionaryDto) {}
29}Podsumowanie modułu
W tym module nauczyłeś się:
- Czym jest OpenAPI/Swagger - standard dokumentacji API
- Instalacja i konfiguracja - DocumentBuilder i SwaggerModule
- Dekoratory kontrolerów - @ApiTags, @ApiOperation, @ApiResponse, @ApiParam, @ApiQuery
- Dokumentacja DTO - @ApiProperty z opisami, przykładami i walidacją
- Zaawansowane odpowiedzi - dedykowane dekoratory dla każdego kodu statusu
- Customization - personalizacja Swagger UI
- CLI Plugin - automatyczne generowanie dokumentacji
- Wersjonowanie - multiple docs dla różnych wersji API
Teraz Twoje Kroniki Imperium są kompletne! Każdy endpoint jest udokumentowany jak cesarski dekret, a Swagger UI służy jako Forum Annałów dla wszystkich developerów.
Ćwiczenie praktyczne
Stwórz kompletną dokumentację API Imperium Romanum:
Kod do tej lekcji: src/app.controller.ts
1// Projekt glowny: Kompletna dokumentacja Imperium API
2import { Controller, Get, Post, Put, Delete, Param, Body, Query } from '@nestjs/common';
3import {
4 ApiTags, ApiOperation, ApiParam, ApiQuery, ApiBearerAuth,
5 ApiOkResponse, ApiCreatedResponse, ApiNotFoundResponse,
6 ApiBadRequestResponse, ApiUnauthorizedResponse,
7} from '@nestjs/swagger';
8import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
9
10// ===== DTO =====
11
12enum LegionaryRank {
13 MILES = 'Miles',
14 OPTIO = 'Optio',
15 CENTURIO = 'Centurio',
16 TRIBUNUS = 'Tribunus',
17 LEGATUS = 'Legatus',
18}
19
20class CreateLegionaryDto {
21 @ApiProperty({ description: 'Imie legionisty', example: 'Marcus Aurelius' })
22 name: string;
23
24 @ApiProperty({ description: 'Ranga', enum: LegionaryRank, example: LegionaryRank.MILES })
25 rank: LegionaryRank;
26
27 @ApiProperty({ description: 'Przypisany legion', example: 'Legio X Gemina' })
28 legio: string;
29
30 @ApiPropertyOptional({ description: 'Doswiadczenie', example: 3, minimum: 0 })
31 experience?: number;
32}
33
34class LegionaryResponseDto {
35 @ApiProperty({ example: '64a1b2c3d4e5f6a7b8c9d0e1' })
36 id: string;
37
38 @ApiProperty({ example: 'Marcus Aurelius' })
39 name: string;
40
41 @ApiProperty({ enum: LegionaryRank })
42 rank: LegionaryRank;
43
44 @ApiProperty({ example: 'Legio X Gemina' })
45 legio: string;
46
47 @ApiProperty({ example: 5 })
48 experience: number;
49}
50
51// ===== KONTROLER =====
52
53@ApiTags('Legiones')
54@Controller('legiones')
55class LegionController {
56 @ApiOperation({ summary: 'Pobierz wszystkie legiony' })
57 @ApiOkResponse({ description: 'Lista legionow', type: [LegionaryResponseDto] })
58 @ApiQuery({ name: 'rank', required: false, enum: LegionaryRank })
59 @Get()
60 findAll(@Query('rank') rank?: LegionaryRank) {
61 return [
62 { id: '1', name: 'Marcus', rank: 'Centurio', legio: 'Legio X', experience: 10 },
63 ];
64 }
65
66 @ApiOperation({ summary: 'Pobierz legioniste po ID' })
67 @ApiParam({ name: 'id', description: 'ID legionisty' })
68 @ApiOkResponse({ type: LegionaryResponseDto })
69 @ApiNotFoundResponse({ description: 'Legionista nie znaleziony' })
70 @Get(':id')
71 findOne(@Param('id') id: string) {
72 return { id, name: 'Marcus', rank: 'Centurio', legio: 'Legio X', experience: 10 };
73 }
74
75 @ApiBearerAuth('JWT-auth')
76 @ApiOperation({ summary: 'Rekrutuj legioniste' })
77 @ApiCreatedResponse({ type: LegionaryResponseDto })
78 @ApiBadRequestResponse({ description: 'Nieprawidlowe dane' })
79 @ApiUnauthorizedResponse({ description: 'Brak autoryzacji' })
80 @Post()
81 create(@Body() dto: CreateLegionaryDto) {
82 return { id: '2', ...dto, experience: dto.experience || 0 };
83 }
84}
85
86console.log("Kompletna dokumentacja Imperium API");
87console.log("DTO: CreateLegionaryDto, LegionaryResponseDto");
88console.log("Kontroler: LegionController z pelna dokumentacja");
89console.log("Dekoratory: Tags, Operation, Param, Query, Response, Auth");
90Widzisz błąd w tej lekcji?
Sprawdź się
Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.
1. Które dekoratory Swagger powinien mieć dobrze udokumentowany endpoint GET /:id?
2. Jak najlepiej zabezpieczyć dostęp do Swagger UI w środowisku produkcyjnym?
To 2 z 4 pytań do tej lekcji. Pozostałe rozwiążesz w grze.
Zadania praktyczne w grze
- Układanie w pionie
Uporządkuj elementy kompletnej dokumentacji API od konfiguracji do prezentacji:
- Edytor kodu
Stwórz CreateArmyDto z enum ArmyUnit, zagnieżdżonym CommanderDto, tablicą provinces i walidacją soldiers
- Klikanie w kolejności
Ułóż elementy udokumentowanego pola DTO od dekoratora do typu:
- Układanie w poziomie
Ułóż argumenty SwaggerModule.setup() w prawidłowej kolejności:
- Układanie w pionie
Uporządkuj strategie wersjonowania od najprostszej do najbardziej zaawansowanej:
- Klikanie w kolejności
Ułóż elementy importu dekoratorów Swagger w prawidłowej kolejności:
- Układanie w pionie
Uporządkuj etapy tworzenia dokumentacji Swagger od instalacji do korzystania:
- Układanie w poziomie
Ułóż elementy dekoratora @ApiResponse w prawidłowej kolejności: