Kurs NestJS · Moduł 11: Swagger i OpenAPI

Projekt główny: Kompletna dokumentacja Imperium API

3 min czytania
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ę:

  1. Czym jest OpenAPI/Swagger - standard dokumentacji API
  2. Instalacja i konfiguracja - DocumentBuilder i SwaggerModule
  3. Dekoratory kontrolerów - @ApiTags, @ApiOperation, @ApiResponse, @ApiParam, @ApiQuery
  4. Dokumentacja DTO - @ApiProperty z opisami, przykładami i walidacją
  5. Zaawansowane odpowiedzi - dedykowane dekoratory dla każdego kodu statusu
  6. Customization - personalizacja Swagger UI
  7. CLI Plugin - automatyczne generowanie dokumentacji
  8. 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");
90

Widzisz błąd w tej lekcji?

Sprawdź się

Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.

  1. 1. Które dekoratory Swagger powinien mieć dobrze udokumentowany endpoint GET /:id?

  2. 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:

Przydatne artykuły