Dekret bez pieczęci to kartka. Nie wiadomo, kto go wydał, czego dotyczy ani czy w ogóle obowiązuje - a urzędnik w prowincji musi to rozstrzygnąć, zanim cokolwiek zrobi.
Endpoint bez opisu jest dokładnie taki sam. Swagger sam odczyta z kodu ścieżkę i metodę HTTP, ale nie odgadnie, co ten endpoint robi, które parametry są obowiązkowe ani co oznacza zwrócony przez niego kod. Dekoratory z pakietu
@nestjs/swagger dopisują to, czego z samego kodu nie widać.Ważna uwaga na początek: wszystkie te dekoratory tylko opisują. Żaden nie zmienia zachowania aplikacji - endpoint bez
@ApiOperation działa tak samo, po prostu w dokumentacji jest bezimienny.Pierwsze dwie pieczęcie odpowiadają na pytania „gdzie to szukać" i „co to robi":
1@ApiTags('Prowincje')
2@Controller('provinces')
3export class ProvincesController {
4 @ApiOperation({
5 summary: 'Lista prowincji',
6 description: 'Zwraca wszystkie prowincje Imperium wraz z ich namiestnikami.',
7 })
8 @Get()
9 findAll() {
10 return this.provincesService.findAll();
11 }
12}
grupuje endpointy w sekcje - w Swagger UI powstaje z tego rozwijana grupa. Stawiasz go nad klasą kontrolera, bo grupowanie dotyczy wszystkich jego metod naraz. Można podać kilka tagów po przecinku, gdy kontroler należy do dwóch obszarów.@ApiTags()
przyjmuje dwie główne właściwości: @ApiOperation()
i summary
. Nie description
url i method - te NestJS odczytuje z @Get i @Controller. Nie host i port - to konfiguracja serwera, nie opis endpointu. I nie controller ani service - to nazwy klas, o których dokumentacja nie musi wiedzieć.Różnica między nimi jest praktyczna:
summary to jedno zdanie widoczne na liście endpointów, description - dłuższy tekst rozwijany po kliknięciu. Jeśli masz napisać tylko jedno, pisz summary.Te dwa dekoratory mylą się najczęściej, bo oba opisują „coś, co przychodzi w adresie". Rozstrzyga miejsce w URL-u:
1@ApiParam({ name: 'id', description: 'Identyfikator prowincji' })
2@Get(':id')
3findOne(@Param('id') id: string) {
4 return this.provincesService.findOne(id);
5}
opisuje parametry w ścieżce URL (route parameters) - te, które w trasie zapisujesz z dwukropkiem, jak @ApiParam()
:id, i które są częścią adresu. W /provinces/7 siódemka jest parametrem ścieżki.
opisuje parametry zapytania - te po znaku @ApiQuery()
w URL. W ?
/provinces?region=Gallia&page=2 parametrami zapytania są region i page. Nie wykonuje żadnego zapytania do bazy danych, nie definiuje schematu GraphQL i nie tworzy query buildera dla ORM-a - słowo „query" odnosi się tu wyłącznie do części adresu.Pozostałe miejsca mają własne dekoratory: nagłówki HTTP opisuje
@ApiHeader, a ciało żądania (request body) - @ApiBody. Cztery różne miejsca, cztery różne dekoratory; pomyłka nie da błędu, tylko dokumentację opisującą coś, czego tam nie ma.Parametry zapytania bywają opcjonalne i właśnie to trzeba w dokumentacji zaznaczyć:
1@ApiQuery({
2 name: 'province',
3 required: false,
4})
5@Get('search')
6search(@Query('province') province?: string) {
7 return this.provincesService.search(province);
8}Kolejność zapisu jest stała:
otwiera dekorator, @ApiQuery({
podaje nazwę parametru - to jedyne pole obowiązkowe - name: 'province',
oznacza go jako opcjonalny, a required: false
domyka.})
required domyślnie ma wartość true, więc opcjonalność trzeba zaznaczyć jawnie. Pominięcie tego jest drobiazgiem, który kosztuje cudzy czas: w Swagger UI parametr pokaże się jako wymagany, a ktoś zbuduje klienta wysyłającego region przy każdym wywołaniu.Gdy zbierzemy wszystko razem, dekoratory układają się od najogólniejszego do najbardziej szczegółowego:
1@ApiTags('Prowincje')
2@Controller('provinces')
3export class ProvincesController {
4 @ApiOperation({ summary: 'Szczegóły prowincji' })
5 @ApiResponse({ status: 200, description: 'Prowincja odnaleziona' })
6 @ApiResponse({ status: 404, description: 'Nie ma takiej prowincji' })
7 @ApiParam({ name: 'id', description: 'Identyfikator prowincji' })
8 @ApiBearerAuth('JWT-auth')
9 @Get(':id')
10 findOne(@Param('id') id: string) {
11 return this.provincesService.findOne(id);
12 }
13}Czyta się to z góry na dół:
na klasie kontrolera obejmuje wszystkie metody, potem nad konkretną metodą @ApiTags('Nazwa')
mówi, co ona robi, następnie @ApiOperation({ summary })
wylicza możliwe odpowiedzi, a na końcu wykonuje się ciało metody.@ApiResponse({ status })
przyjmuje kod statusu i opis; można go podać wiele razy, po jednym na każdą możliwą odpowiedź. W następnych lekcjach zastąpimy go krótszymi odpowiednikami w rodzaju @ApiResponse()
@ApiOkResponse.
oznacza endpoint jako chroniony - w Swagger UI pojawi się przy nim pole na token, a podana nazwa musi zgadzać się z konfiguracją @ApiBearerAuth('JWT-auth')
addBearerAuth.Dekret bez pieczęci to kartka, @name:
@ApiTags() grupuje endpointy i stoi nad klasą kontrolera,@ApiOperation() przyjmuje summary i description - nie url/method, nie host/port, nie controller/service,@ApiParam() opisuje parametry w ścieżce URL (te z dwukropkiem, jak :id),@ApiQuery() opisuje parametry zapytania po znaku ? - nie odpytuje bazy, nie definiuje GraphQL-a, nie buduje zapytań ORM,@ApiHeader, ciało żądania - @ApiBody,@ApiQuery: @ApiQuery({ → name: 'province', → required: false → }); bez required: false parametr uchodzi za wymagany,@ApiTags('Nazwa') na klasie → @ApiOperation({ summary }) nad metodą → @ApiResponse({ status }) nad metodą → ciało metody,@ApiBearerAuth('JWT-auth') oznacza endpoint chroniony tokenem.W następnej lekcji zejdziemy poziom niżej - do dokumentowania samych DTO, czyli tego, co w tych żądaniach i odpowiedziach naprawdę siedzi. A na razie zapamiętaj: dokumentacja opisuje to, co napisałeś w dekoratorze, nie to, co robi kod. Rozjazd między nimi jest gorszy niż brak dokumentacji, bo w ten pierwszy ktoś uwierzy.