Go a ekosystém OpenAPI
Úvod
Pri vývoji Production Backend servera v jazyku Go sa takmer všetci vývojári stretávajú s jednou z prvých výziev, ktorá znie nasledovne:
Ako dokumentovať API...?
Pri bližšom skúmaní si človek uvedomí, že je výhodné písať dokumentáciu v súlade so špecifikáciou OpenAPI, a prirodzene začne hľadať knižnice, ktoré sa integrujú s OpenAPI. Aj keď sa toto rozhodnutie prijme, existuje ďalší problém:
Je tu toľko knižníc súvisiacich s OpenAPI... Ktorú by som mal použiť...?
Tento dokument je stručným predstavením knižníc, napísaným pre začiatočníkov v Go, ktorí zažívajú túto situáciu. Dokument bol napísaný koncom roka 2024 a keďže ekosystém jazyka sa neustále mení, odporúča sa ho použiť ako referenciu a zároveň sledovať najnovšie trendy.
Stratégie knižníc pristupujúcich k OpenAPI
Ako už možno viete, OpenAPI je špecifikácia na jasné definovanie a dokumentovanie REST API. Definuje koncové body API, formáty požiadaviek a odpovedí vo formáte YAML alebo JSON, čo pomáha nielen vývojárom, ale aj automatizácii generovania kódu na strane frontendu a backendu, čím sa znižuje zbytočná opakovateľnosť a malé ľudské chyby.
Na prirodzené prepojenie OpenAPI s projektom prijímajú knižnice v ekosystéme Go predovšetkým nasledujúce tri stratégie.
1. Kombinácia Go komentárov do dokumentu špecifikácie OpenAPI
Jednou z náročných vecí pri vývoji API v súlade s OpenAPI je, že skutočný dokument a kód implementujúci tento dokument existujú v samostatných súboroch na úplne odlišných miestach. V dôsledku toho je dosť časté, že sa kód aktualizuje, ale dokumentácia sa neaktualizuje, alebo sa dokumentácia aktualizuje, ale kód sa neaktualizuje.
Pre jednoduchý príklad:
- Ak upravíte logiku pre API v súbore
./internal/server/user.go, - a skutočná dokumentácia existuje v
./openapi3.yaml, môžete náhodou zabudnúť na zmeny v nej. - Ak odošlete Pull Request bez toho, aby ste si uvedomili problém s týmito zmenami, a požiadate kolegov o recenziu,
- recenzenti tiež nevidia zmeny v
./openapi3.yaml, čo môže viesť k nepríjemnej situácii, keď špecifikácia API zostáva rovnaká, ale skutočná implementácia API sa zmení.
Vytváranie dokumentácie API vo forme Go komentárov môže tento problém do určitej miery vyriešiť. Keďže kód a dokumentácia sú na jednom mieste, môžete aktualizovať kód a komentáre súčasne. Existujú nástroje, ktoré automaticky generujú dokumentáciu špecifikácie OpenAPI na základe týchto komentárov.
Reprezentatívnym projektom je Swag. Swag analyzuje komentáre v Go kóde a generuje dokumentáciu vo formáte OpenAPI 2. Použitie je jednoduché. Stačí napísať komentáre nad funkciu handleru v súlade s formátom definovaným v každej knižnici.
1// @Summary Vytvorenie používateľa
2// @Description Vytvorí nového používateľa.
3// @Tags Users
4// @Accept json
5// @Produce json
6// @Param user body models.User true "Informácie o používateľovi"
7// @Success 200 {object} models.User
8// @Failure 400 {object} models.ErrorResponse
9// @Router /users [post]
10func CreateUser(c *gin.Context) {
11 // ...
12}
Keď sú komentáre napísané týmto spôsobom, CLI nástroj Swag ich analyzuje a generuje dokumentáciu OpenAPI 2. Táto operácia sa zvyčajne vykonáva v rámci procesu CI a vygenerovaná dokumentácia špecifikácie OpenAPI sa nasadzuje do Git Repository, konečného výsledku zostavenia alebo samostatného externého systému správy dokumentácie API, kde sa používa na spoluprácu s inými projektmi.
Výhody:
- Keďže komentáre sú spolu s kódom, znižuje sa pravdepodobnosť rozdielov medzi skutočným kódom a dokumentáciou.
- Dokumentáciu je možné jednoducho a slobodne vytvárať iba pomocou komentárov, bez potreby samostatných nástrojov alebo komplexnej konfigurácie.
- Keďže komentáre neovplyvňujú skutočnú logiku API, sú vhodné na pridávanie dočasných funkcií, ktoré by bolo príliš náročné zverejniť v dokumentácii.
Nevýhody:
- S rastúcim počtom riadkov komentárov môže klesnúť čitateľnosť jedného súboru kódu.
- Môže byť ťažké vyjadriť celú špecifikáciu API vo forme komentárov.
- Keďže dokumentácia nevynucuje kód, nie je možné zaručiť, že dokumentácia OpenAPI a skutočná logika sa zhodujú.
2. Generovanie Go kódu z dokumentu špecifikácie OpenAPI
Existuje aj prístup, kde Single source of Truth (SSOT) nie je Go kód, ale dokumentácia. Spočíva v tom, že sa najprv definuje špecifikácia OpenAPI a na základe definovaného obsahu sa generuje Go kód. Keďže špecifikácia API generuje kód, môže to kultúrne vynútiť najprv navrhnúť API. Keďže definovanie špecifikácie API je prvým krokom vo vývojovom procese, má to výhodu, že sa predchádza nepríjemným situáciám, kedy sa po dokončení vývoja zistia premeškané časti a celý kód sa musí upraviť spolu so zmenou špecifikácie API.
Reprezentatívne projekty, ktoré prijímajú tento prístup, sú oapi-codegen a OpenAPI Generator. Použitie je jednoduché.
- Napíšte yaml alebo json dokument v súlade so špecifikáciou OpenAPI.
- Spustite CLI.
- Vygeneruje sa zodpovedajúci Go stub kód.
- Teraz stačí implementovať iba podrobnú logiku pre jednotlivé API, aby sa tento stub mohol použiť.
Nasleduje príklad kódu generovaného oapi-codegen.
1// StrictServerInterface predstavuje všetky serverové handlery.
2type StrictServerInterface interface {
3 // ...
4 // Vráti všetky zvieratá
5 // (GET /pets)
6 FindPets(ctx context.Context, request FindPetsRequestObject) (FindPetsResponseObject, error)
7 // ...
8}
Kód generovaný oapi-codegen na základe vyššie uvedeného rozhrania vykonáva logiku ako parsovanie query parameters, hlavičiek, tela a validáciu, a volá príslušnú metódu deklarovanú v rozhraní. Používateľ dokončí prácu potrebnú na implementáciu API implementáciou tohto rozhrania.
Výhody:
- Keďže najprv vznikne špecifikácia a potom sa pokračuje vo vývoji, je to výhodné pre paralelnú prácu pri spolupráci viacerých tímov.
- Kód pre opakujúce sa, monotónne úlohy sa generuje automaticky, čo zvyšuje efektivitu práce a zároveň je stále výhodné pre ladenie.
- Je ľahké zaručiť, že dokumentácia a kód sú vždy v súlade.
Nevýhody:
- Ak ste neznalí samotnej špecifikácie OpenAPI, počiatočná krivka učenia môže byť značná.
- Keďže forma kódu, ktorá spracováva API, sa generuje automaticky projektom, môže byť ťažké prispôsobiť sa, ak je potrebné prispôsobenie.
Komentár autora. K októbru 2024 Go kód generovaný OpenAPI Generatorom nielenže vynucuje logiku API, ale aj celkovú štruktúru projektu, čo vedie k rigidnej štruktúre projektu. Generuje kód v tvare, ktorý je nevhodný na pridávanie rôznych funkcií potrebných v skutočnom Production prostredí. Tým, ktorí si zvolia tento prístup, dôrazne odporúčam použiť oapi-codegen. Autor používa oapi-codege + echo + StrictServerInterface.
3. Generovanie dokumentu špecifikácie OpenAPI z Go kódu
Keď stovky, či desiatky ľudí vyvíjajú ten istý server, nevyhnutne nastane problém, že jednotlivé API môžu stratiť konzistenciu. Intuitívnym príkladom je situácia, kedy sa špecifikácia pre viac ako 100 API Endpointov deklaruje v jednom súbore OpenAPI yaml. Tento súbor sa stane monštrom s viac ako 10 000 riadkami a nevyhnutne sa začne narúšať celková konzistentnosť API, napríklad duplicitné deklarovanie rovnakého modelu pri deklarovaní nového API Endpointu, vynechanie niektorých polí alebo vytvorenie názvov ciest, ktoré nie sú v súlade s konvenciami.
Na vyriešenie tohto problému je možné prideliť samostatného Ownera na správu OpenAPI yaml, alebo vyvinúť Linter, ktorý automaticky identifikuje problémy počas procesu CI. Avšak, definovaním Domain-specific language (DSL) v jazyku Go je možné vynútiť, aby všetky API mali konzistentnú jednotnosť.
Reprezentatívnym projektom, ktorý používa túto techniku, je Kubernetes (postavený interne bez samostatných knižníc), a môžete to vyskúšať pomocou projektov ako go-restful a goa. Nasleduje príklad použitia goa.
1var _ = Service("user", func() {
2 Method("create", func() {
3 Payload(UserPayload)
4 Result(User)
5 HTTP(func() {
6 POST("/users")
7 Response(StatusOK)
8 })
9 })
10})
Napísaním kompilovateľného Go kódu, ako je uvedené vyššie, získate výhodu, že implementácia API pre POST /users a definícia dokumentácie sa dokončia súčasne.
Výhody:
- Keďže všetko vychádza z kódu, je ľahké udržať konzistenciu API v celom projekte.
- Využitím silného typového systému Go môžete získať presnejšiu a nespochybniteľnú špecifikáciu, než pri použití všetkých funkcií OpenAPI3.
Nevýhody:
- Je potrebné naučiť sa DSL definované v jednotlivých frameworkoch a môže byť ťažké ho aplikovať na existujúci kód.
- Je potrebné násilne dodržiavať pravidlá navrhnuté frameworkom, čo môže znížiť slobodu a flexibilitu.
Záverom
Každá metóda má svoje výhody a nevýhody a je dôležité vybrať si tú najvhodnejšiu metódu podľa požiadaviek projektu a preferencií tímu. Najdôležitejšie nikdy nie je to, aký prístup je najlepší, ale skôr posúdiť, ktoré riešenie je najvhodnejšie pre vašu aktuálnu situáciu, a zvýšiť produktivitu vývoja, aby ste si mohli užiť rýchly odchod z práce a uspokojivú rovnováhu medzi pracovným a súkromným životom.
Hoci tento článok bol napísaný v októbri 2024, ekosystém Go a OpenAPI sa neustále vyvíja, preto odporúčam, aby ste s ohľadom na časový odstup od prečítania tohto článku priebežne sledovali aktuálny stav jednotlivých knižníc a projektov, ako aj zmenené výhody a nevýhody.
Prajem vám šťastný Go život~ 😘