GoSuda

Go și ecosistemul OpenAPI

By iwanhae
views ...

Introducere

Atunci când dezvoltă un server backend de producție în limbajul Go, majoritatea dezvoltatorilor se confruntă cu o dificultate inițială majoră:

Cum documentăm API-urile...?

În urma unei investigații sumare, se constată că este benefic să se scrie documentația conform specificației OpenAPI și, în mod firesc, se caută o bibliotecă care să se integreze cu OpenAPI. Cu toate acestea, chiar și după luarea acestei decizii, apare următoarea problemă:

Există multe biblioteci legate de OpenAPI... pe care ar trebui să o folosesc...?

Acest document este o scurtă introducere a bibliotecilor, scrisă pentru începătorii în Go care se confruntă cu această situație. Acest document a fost scris la sfârșitul anului 2024 și, deoarece ecosistemul limbajului este în continuă schimbare, este recomandat să-l utilizați ca referință și să urmăriți întotdeauna ultimele noutăți.

Strategiile bibliotecilor în abordarea OpenAPI

După cum probabil știți deja, OpenAPI este o specificație pentru definirea și documentarea clară a API-urilor REST. Prin definirea punctelor finale ale API-ului, a formatelor de cerere și răspuns în format YAML sau JSON, se reduce repetarea inutilă și se minimizează erorile umane, automatizând generarea de cod nu doar pentru dezvoltatori, ci și pentru front-end și back-end.

Pentru a integra în mod natural OpenAPI cu proiectul, bibliotecile din ecosistemul Go adoptă în general următoarele trei strategii majore:

1. Combinarea comentariilor Go în documente de specificație OpenAPI

Unul dintre aspectele dificile în dezvoltarea API-urilor în conformitate cu OpenAPI este că documentul real și codul care implementează acel document există în locații complet diferite ca fișiere separate. Acest lucru duce destul de frecvent la situații în care codul este actualizat, dar documentul nu este, sau documentul este actualizat, dar codul nu.

De exemplu:

  1. Să presupunem că logica pentru API este modificată într-un fișier numit ./internal/server/user.go.
  2. Documentul real se află în ./openapi3.yaml și este posibil să se uite modificarea acestuia din greșeală.
  3. Dacă se trimite o Pull Request fără a realiza această problemă și se cere o recenzie colegilor,
  4. deoarece recenzorii nu văd nici modificările din ./openapi3.yaml, se poate întâmpla nefericita situație în care specificația API-ului rămâne aceeași, dar implementarea API-ului real este modificată.

Scrierea documentației API sub formă de comentarii Go poate rezolva într-o oarecare măsură această problemă. Deoarece codul și documentul sunt împreună într-un singur loc, comentariile pot fi actualizate odată cu modificarea codului. Există instrumente care generează automat documente de specificație OpenAPI bazate pe aceste comentarii.

Un exemplu tipic de proiect este Swag. Swag analizează comentariile din codul Go și generează documente în format OpenAPI 2. Utilizarea este simplă. Comentariile trebuie scrise în formatul specificat de fiecare bibliotecă deasupra funcțiilor de tip handler.

 1// @Summary Creare utilizator
 2// @Description Creează un nou utilizator.
 3// @Tags Users
 4// @Accept json
 5// @Produce json
 6// @Param user body models.User true "Informații utilizator"
 7// @Success 200 {object} models.User
 8// @Failure 400 {object} models.ErrorResponse
 9// @Router /users [post]
10func CreateUser(c *gin.Context) {
11    // ...
12}

Când comentariile sunt scrise în acest mod, CLI-ul Swag analizează aceste comentarii și generează documentul OpenAPI 2. În general, această operațiune este efectuată în timpul procesului de CI, iar documentul specificației OpenAPI generat este distribuit în Git Repository, rezultatul final al compilării și un sistem extern separat de gestionare a documentelor API, fiind utilizat pentru colaborarea cu alte proiecte.

Avantaje:

  • Deoarece comentariile sunt împreună cu codul, probabilitatea ca forma codului real și a documentului să difere este redusă.
  • Documentația poate fi creată simplu și liber doar cu comentarii, fără instrumente separate sau setări complicate.
  • Deoarece comentariile nu afectează logica API-ului real, este benefic pentru adăugarea de caracteristici temporare care nu sunt ușor de publicat ca documente.

Dezavantaje:

  • Pe măsură ce numărul de linii de comentarii crește, lizibilitatea unui singur fișier de cod poate scădea.
  • Poate fi dificil de exprimat întreaga specificație API sub formă de comentarii.
  • Deoarece documentul nu forțează codul, nu există nicio garanție că documentul OpenAPI și logica reală coincid.

2. Generarea de cod Go din documente de specificație OpenAPI

Există o modalitate de a pune sursa unică de adevăr (Single Source of Truth - SSOT) nu în codul Go, ci în document. Aceasta este metoda de a defini mai întâi specificația OpenAPI și apoi de a genera cod Go pe baza conținutului definit. Deoarece specificația API generează codul, este posibil să se impună o cultură de dezvoltare în care proiectarea API-ului este realizată mai întâi. De asemenea, deoarece definirea specificației API este primul pas în ordinea de dezvoltare, se poate preveni o problemă, și anume ca părțile care sunt omise să fie realizate după finalizarea dezvoltării, iar întregul cod să fie modificat odată cu modificarea specificației API.

Printre proiectele reprezentative care adoptă această metodă se numără oapi-codegen și OpenAPI Generator. Utilizarea este simplă:

  1. Se scrie un document yaml sau json conform specificației OpenAPI,
  2. se rulează CLI-ul și
  3. se generează codul Go stub corespunzător.
  4. Acum, trebuie doar să implementați direct logica detaliată pentru fiecare API pentru ca acest stub să poată fi utilizat.

Următorul este un exemplu de cod generat de oapi-codegen.

1// StrictServerInterface reprezintă toți handlerii serverului.
2type StrictServerInterface interface {
3	// ...
4	// Returnează toate animalele de companie
5	// (GET /pets)
6	FindPets(ctx context.Context, request FindPetsRequestObject) (FindPetsResponseObject, error)
7	// ...
8}

Prin intermediul acestei interfețe, codul generat de oapi-codegen execută logica de analiză și validare a parametrilor de interogare, a antetului și a corpului și apelează metoda adecvată declarată în interfață. Utilizatorul trebuie doar să implementeze implementarea pentru această interfață, iar activitatea necesară pentru implementarea API-ului este finalizată.

Avantaje:

  • Deoarece specificația vine prima, iar dezvoltarea urmează, este mai ușor să se desfășoare munca în paralel atunci când mai multe echipe colaborează.
  • Deoarece codul pentru părțile care erau realizate prin muncă repetitivă este generat automat, eficiența muncii crește, dar depanarea este încă ușoară.
  • Este ușor să se garanteze că forma documentului și a codului coincide întotdeauna.

Dezavantaje:

  • Dacă nu există cunoștințe despre specificația OpenAPI în sine, curba inițială de învățare este oarecum abruptă.
  • Deoarece forma codului care gestionează API-ul este generată automat de proiect, poate fi dificil să se răspundă dacă este necesară personalizarea.

Comentariul autorului. Începând cu octombrie 2024, codul Go generat de OpenAPI Generator forțează forma întregului proiect, nu doar logica API-ului, iar structura proiectului este rigidă, astfel încât generează un cod care nu este potrivit pentru adăugarea diferitelor funcții necesare într-un mediu de producție real. Cei care adoptă această metodă sunt încurajați să utilizeze oapi-codegen. Autorul folosește oapi-codege + echo + StrictServerInterface.

3. Generarea documentelor de specificație OpenAPI cu cod Go

O problemă care apare inevitabil atunci când zeci sau sute de persoane dezvoltă același server este că uniformitatea poate fi compromisă pentru fiecare API în parte. Un exemplu intuitiv este că, dacă specificațiile pentru mai mult de 100 de puncte finale API sunt declarate într-un singur fișier OpenAPI yaml, acel fișier va deveni un monstru de peste 10.000 de linii, iar pe măsură ce un nou punct final API este declarat, același model va fi în mod inevitabil declarat de mai multe ori, unele câmpuri vor fi omise sau vor apărea denumiri de cale care nu sunt conforme cu convențiile, ceea ce va începe să strice uniformitatea generală a API-ului.

Pentru a rezolva aceste probleme, se poate desemna un proprietar separat pentru gestionarea fișierului OpenAPI yaml, se poate dezvolta un Linter pentru a-l prinde automat în timpul procesului de CI, dar se poate defini și un limbaj specific domeniului (DSL) în limbajul Go pentru a impune ca toate API-urile să aibă o uniformitate consistentă.

Un proiect reprezentativ care utilizează această tehnică este Kubernetes (construit independent, fără bibliotecă separată), și poate fi utilizat și cu proiecte precum go-restful și goa. Următorul este un exemplu de utilizare a 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})

Atunci când se scrie cod Go compilabil ca mai sus, se obține avantajul de a finaliza simultan implementarea și definiția documentației pentru API-ul POST /users.

Avantaje:

  • Deoarece totul provine din cod, este mai ușor să se mențină uniformitatea API-ului pentru întregul proiect.
  • Prin utilizarea sistemului de tip puternic Go, se poate obține o specificație mai precisă și incontestabilă decât atunci când sunt utilizate toate funcțiile OpenAPI3.

Dezavantaje:

  • Este necesar să se învețe DSL-ul definit în fiecare cadru de lucru, iar aplicarea la codul existent poate fi dificilă.
  • Deoarece regulile propuse de cadru de lucru trebuie urmate cu forța, libertatea și flexibilitatea pot fi reduse.

În încheiere

Fiecare metodă are avantaje și dezavantaje, iar alegerea celei potrivite depinde de cerințele proiectului și de preferințele echipei. Cel mai important lucru este să nu vă întrebați ce metodă este mai bună, ci să evaluați care este cea mai bună soluție pentru situația actuală, să creșteți productivitatea dezvoltării și să vă bucurați de o ieșire rapidă de la muncă și de un echilibru satisfăcător între viața profesională și cea personală.

Acest articol a fost scris în octombrie 2024, dar ecosistemul Go și OpenAPI este în continuă dezvoltare, așa că vă rugăm să urmăriți în mod continuu ultimele noutăți despre biblioteci și proiecte și avantajele și dezavantajele modificate ale acestora, ținând cont de diferența de timp dintre momentul în care a fost scris acest articol și momentul în care îl citiți.

Să aveți o viață fericită în Go~ 😘