GoSuda

Go ja OpenAPI-ekosysteemi

By iwanhae
views ...

Johdanto

Kun Go-kielellä kehitetään Production Backend -palvelinta, lähes kaikki kehittäjät kohtaavat yhden ensimmäisistä haasteista:

Miten API-dokumentaatio tehdään?

Hieman asiaa tutkittuaan he ymmärtävät, että on edullista laatia OpenAPI-spesifikaation mukainen dokumentaatio, ja luonnollisesti he alkavat etsiä OpenAPI:n kanssa yhteensopivia kirjastoja. Vaikka tämä päätös tehdään, seuraava ongelma jää:

OpenAPI-kirjastoja on paljon... mitä minun pitäisi käyttää?

Tämä dokumentti on lyhyt esittely kirjastoista Go-aloittelijoille, jotka kokevat tällaisen tilanteen. Dokumentti on kirjoitettu vuoden 2024 lopun tilanteen mukaan, ja koska kieliekosysteemi muuttuu jatkuvasti, on suositeltavaa pitää tämä mielessä ja seurata aina uusimpia kehityksiä.

Kirjastojen strategiat OpenAPI:n käsittelyssä

Kuten ehkä jo tiedättekin, OpenAPI on spesifikaatio, jolla REST API:t määritellään ja dokumentoidaan selkeästi. Se määrittelee API:n päätepisteet, pyynnöt ja vastausmuodot YAML- tai JSON-muodossa, mikä auttaa paitsi kehittäjiä myös automatisoimaan front-end- ja back-end-koodin luomista, vähentäen turhaa toistoa ja pieniä inhimillisiä virheitä.

Jotta OpenAPI saadaan integroitua luontevasti projektiin, Go-ekosysteemin kirjastot käyttävät yleensä seuraavia kolmea strategiaa:

1. Go-kommenttien yhdistäminen OpenAPI-spesifikaation dokumentiksi

Yksi haasteista API:n kehittämisessä OpenAPI:n mukaisesti on se, että todellinen dokumentaatio ja sitä toteuttava koodi ovat eri tiedostoissa ja sijainneissa. Tämän vuoksi on yllättävän usein tilanteita, joissa koodia päivitetään, mutta dokumentaatiota ei, tai dokumentaatiota päivitetään, mutta koodia ei.

Yksinkertainen esimerkki:

  1. API:n logiikkaa muutetaan tiedostossa ./internal/server/user.go.
  2. Todellinen dokumentaatio sijaitsee tiedostossa ./openapi3.yaml, ja tähän liittyvät muutokset saattavat unohtua vahingossa.
  3. Jos Pull Request lähetetään ja kollegat tarkistavat sen ilman, että tätä muutosta koskevaa ongelmaa tiedostetaan,
  4. tarkastelijatkaan eivät näe muutoksia tiedostossa ./openapi3.yaml, mikä voi johtaa siihen, että API-spesifikaatio pysyy samana, mutta todellinen API-toteutus muuttuu.

API-dokumentaation kirjoittaminen Go-kommenttien muodossa voi ratkaista tämän ongelman jossain määrin. Koska koodi ja dokumentaatio ovat samassa paikassa, kommentit voidaan päivittää samanaikaisesti koodin muokkaamisen kanssa. On olemassa työkaluja, jotka luovat automaattisesti OpenAPI-spesifikaation dokumentteja näiden kommenttien perusteella.

Tyypillinen projekti on Swag. Swag jäsentää Go-koodin kommentit ja luo OpenAPI 2 -muotoisen dokumentin. Käyttö on yksinkertaista: kirjoita kommentit kunkin kirjaston määrittämän muodon mukaisesti käsittelijäfunktion yläpuolelle.

 1// @Summary Luo käyttäjä
 2// @Description Luo uuden käyttäjän.
 3// @Tags Users
 4// @Accept json
 5// @Produce json
 6// @Param user body models.User true "Käyttäjätiedot"
 7// @Success 200 {object} models.User
 8// @Failure 400 {object} models.ErrorResponse
 9// @Router /users [post]
10func CreateUser(c *gin.Context) {
11    // ...
12}

Kun kommentit on kirjoitettu näin, Swag CLI jäsentää ne ja luo OpenAPI 2 -dokumentin. Yleensä tämä tapahtuma suoritetaan CI-prosessin aikana, ja luotu OpenAPI-spesifikaation dokumentti jaetaan Git Repositoryyn, lopulliseen koontitulokseen tai erilliseen ulkoiseen API-dokumentaation hallintajärjestelmään käytettäväksi yhteistyössä muiden projektien kanssa.

Edut:

  • Koska kommentit ovat koodin kanssa, todellisen koodin ja dokumentaation rakenteen eroamisen todennäköisyys pienenee.
  • Dokumentaation voi tehdä yksinkertaisesti ja vapaasti vain kommenteilla ilman erillisiä työkaluja tai monimutkaisia asetuksia.
  • Koska kommentit eivät vaikuta todelliseen API-logiikkaan, ne sopivat hyvin väliaikaisten ominaisuuksien lisäämiseen, joiden julkaiseminen dokumentaatiossa voi olla hankalaa.

Haitat:

  • Kommenttirivien määrän kasvaessa yksittäisen kooditiedoston luettavuus voi heikentyä.
  • Kaikkia API-spesifikaatioita voi olla vaikea ilmaista kommenttien muodossa.
  • Koska dokumentaatio ei pakota koodia, OpenAPI-dokumentaation ja todellisen logiikan yhdenmukaisuutta ei voida taata.

2. Go-koodin luominen OpenAPI-spesifikaation dokumentista

On olemassa myös tapa asettaa Single Source of Truth (SSOT) dokumentaatioon Go-koodin sijaan. Tämä tarkoittaa OpenAPI-spesifikaation ensin määrittämistä ja sitten Go-koodin luomista määritellyn sisällön perusteella. Koska API-spesifikaatio luo koodin, se pakottaa kehityskulttuurissa API-suunnittelun ensin, ja koska API-spesifikaation määrittely on ensimmäinen askel kehitysprosessissa, se tarjoaa edun, että se voi estää ennalta ongelmia, kuten puuttuvien osien havaitsemisen vasta kehityksen valmistuttua ja koko koodin muuttamisen API-spesifikaation muutoksen mukana.

Tämän lähestymistavan käyttämiä tyypillisiä projekteja ovat oapi-codegen ja OpenAPI Generator. Käyttö on yksinkertaista:

  1. Kirjoita yaml- tai json-dokumentti OpenAPI-spesifikaation mukaisesti.
  2. Suorita CLI.
  3. Vastaava Go stub -koodi luodaan.
  4. Nyt sinun tarvitsee vain toteuttaa yksityiskohtainen logiikka kullekin API:lle, jotta tämä stub voi käyttää sitä.

Seuraava on esimerkki oapi-codegenin luomasta koodista:

1// StrictServerInterface edustaa kaikkia palvelimen käsittelijöitä.
2type StrictServerInterface interface {
3	// ...
4	// Palauttaa kaikki lemmikit
5	// (GET /pets)
6	FindPets(ctx context.Context, request FindPetsRequestObject) (FindPetsResponseObject, error)
7	// ...
8}

Oapi-codegenin tämän rajapinnan avulla luoma koodi suorittaa logiikan, kuten kyselyparametrien, otsakkeiden ja rungon jäsennyksen sekä validoinnin, ja kutsuu rajapinnassa määriteltyä sopivaa metodia. Käyttäjän tarvitsee vain toteuttaa tämän rajapinnan toteutus, ja API-toteutukseen tarvittava työ on valmis.

Edut:

  • Koska spesifikaatio tulee ensin ja kehitys etenee sen jälkeen, se on edullista työn rinnakkaistamiseen useiden tiimien yhteistyössä.
  • Koska automaattisesti luodaan koodia osille, jotka olivat toistuvaa manuaalista työtä, työn tehokkuus paranee, ja se on silti edullista virheenkorjaukselle.
  • On helpompi varmistaa, että dokumentaation ja koodin muoto ovat aina yhdenmukaisia.

Haitat:

  • Jos OpenAPI-spesifikaatio on tuntematon, alkuperäinen oppimiskäyrä voi olla melko jyrkkä.
  • Koska API:n käsittelykoodin muoto luodaan automaattisesti projektin toimesta, mukauttamisen tarpeessa voi olla vaikea vastata.

Kirjoittajan kommentti. Lokakuussa 2024 OpenAPI Generatorin luoma Go-koodi pakottaa paitsi API-logiikan myös koko projektin rakenteen, ja projektin rakenne on jäykkä, mikä tekee siitä sopimattoman erilaisten tuotantoympäristössä tarvittavien toimintojen lisäämiseen. Niille, jotka valitsevat tämän lähestymistavan, suosittelen aktiivisesti oapi-codegenin käyttöä. Kirjoittaja käyttää oapi-codegen + echo + StrictServerInterface -yhdistelmää.

3. OpenAPI-spesifikaation dokumentin luominen Go-koodista

Kun kymmenet tai sadat ihmiset kehittävät samaa palvelinta, väistämättä syntyy ongelma, että yksittäisten API:en yhtenäisyys voi rikkoutua. Yksinkertainen esimerkki: jos yli 100 API Endpointin määritykset ilmoitetaan yhdessä OpenAPI yaml -tiedostossa, tiedostosta tulee yli 10 000 rivin hirviö. Uuden API Endpointin ilmoittaminen johtaa väistämättä samaa mallia toistavaan ilmoitukseen, joidenkin kenttien puuttumiseen tai konvention vastaisen Path-nimen syntymiseen, mikä alkaa rikkoa API:n yleistä yhtenäisyyttä.

Tämän ongelman ratkaisemiseksi voidaan nimetä erillinen OpenAPI yaml -tiedoston omistaja tai kehittää Linter, joka tunnistaa virheet automaattisesti CI-prosessin aikana. Go-kielellä voidaan kuitenkin määritellä Domain-specific language (DSL), joka pakottaa kaikki API:t noudattamaan johdonmukaista yhtenäisyyttä.

Tyypillinen projekti, joka käyttää tätä tekniikkaa, on Kubernetes (joka rakentaa sen itse ilman erillisiä kirjastoja), ja sitä voi käyttää myös projekteilla, kuten go-restful ja goa. Seuraava on esimerkki goa:n käytöstä:

 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})

Kirjoittamalla yllä olevan kaltaista käännettävää Go-koodia saavutetaan etu, että POST /users API:n toteutus ja dokumentaation määrittely valmistuvat samanaikaisesti.

Edut:

  • Koska kaikki syntyy koodista, projektin API-yhdenmukaisuus on helppo säilyttää.
  • Hyödyntämällä Go:n vahvasti tyypitettyä järjestelmää saadaan tarkempi ja kiistaton spesifikaatio kuin hyödyntämällä kaikkia OpenAPI3:n ominaisuuksia.

Haitat:

  • On opeteltava kunkin Frameworkin määrittelemä DSL, ja sen soveltaminen olemassa olevaan koodiin voi olla vaikeaa.
  • Koska Frameworkin ehdottamia sääntöjä on noudatettava pakollisesti, vapaus ja joustavuus voivat heikentyä.

Yhteenveto

Kullakin menetelmällä on etunsa ja haittansa, ja on tärkeää valita sopiva menetelmä projektin vaatimusten ja tiimin mieltymysten mukaan. Tärkeintä ei ole se, mikä menetelmä on paras, vaan se, mikä ratkaisu sopii parhaiten nykyiseen tilanteeseen. Tavoitteena on tehdä arvoarviointi ja lisätä kehitystuottavuutta, jotta voi lähteä nopeasti töistä ja nauttia tyydyttävästä työelämän tasapainosta.

Vaikka tämä artikkeli on kirjoitettu lokakuussa 2024, Go- ja OpenAPI-ekosysteemit kehittyvät jatkuvasti. Siksi on suositeltavaa seurata jatkuvasti kirjastojen ja projektien uusimpia kehityksiä ja niiden muuttuneita etuja ja haittoja ottaen huomioon aika, joka on kulunut tämän artikkelin kirjoittamisesta.

Hyvää Go-elämää! 😘