Go ja OpenAPI-ekosysteemi
Johdanto
Kun kehitetään tuotantokäyttöön tarkoitettua backend-palvelinta Go-kielellä, lähes kaikki kehittäjät kohtaavat ensimmäisenä haasteena seuraavan kysymyksen:
Miten API-dokumentaatio hoidetaan...?
Pienen etsinnän jälkeen on selvää, että OpenAPI-spesifikaation mukaisen dokumentin laatiminen on hyödyllistä, ja luonnollisesti etsitään OpenAPI:n kanssa yhteensopivia kirjastoja. Tämän päätöksen jälkeenkin eteen tulee kuitenkin uusi ongelma.
OpenAPI:hin liittyviä kirjastoja on paljon... mitä pitäisi käyttää...?
Tämä dokumentti on lyhyt johdanto kirjastoihin, joka on kirjoitettu Go-aloittelijoille, jotka ovat kohdanneet tämän tilanteen. Dokumentti on kirjoitettu vuoden 2024 lopussa, ja koska kieliekosysteemi muuttuu jatkuvasti, on suositeltavaa käyttää tätä dokumenttia apuna ja seurata aina viimeisimpiä tapahtumia.
Kirjastojen lähestymistavat OpenAPI:hin
Kuten ehkä jo tiedätkin, OpenAPI on spesifikaatio, jonka avulla REST-API:t määritellään ja dokumentoidaan selkeästi. API:n päätepisteet, pyynnöt ja vastausmuodot määritellään YAML- tai JSON-muodossa, mikä auttaa vähentämään merkityksetöntä toistoa ja pieniä inhimillisiä virheitä automatisoimalla koodin luomisen sekä kehittäjille että front- ja backend-puolille.
Jotta OpenAPI voidaan yhdistää projektiin luontevasti, Go-ekosysteemin kirjastot noudattavat pääasiassa kolmea seuraavaa strategiaa.
1. Go-kommenttien yhdistäminen OpenAPI-spesifikaatiodokumentiksi
Yksi haastavista asioista API:n kehittämisessä OpenAPI:n mukaisesti on se, että varsinainen dokumentti ja sen toteuttava koodi ovat erillisissä tiedostoissa eri paikoissa. Näin ollen on yllättävän yleistä, että koodia on päivitetty, mutta dokumenttia ei, tai dokumenttia on päivitetty, mutta koodia ei.
Yksinkertaisena esimerkkinä:
- API:n logiikkaa on muutettu tiedostossa
./internal/server/user.go
- Varsinainen dokumentti sijaitsee tiedostossa
./openapi3.yaml
, ja muutokset on saatettu unohtaa. - Jos näitä muutoksia ei huomata ja Pull Request lähetetään kollegoille arvioitavaksi
- Arvioijat eivät myöskään huomaa muutoksia tiedostossa
./openapi3.yaml
, joten saattaa syntyä epätoivottava tilanne, jossa API-spesifikaatio on ennallaan, mutta varsinainen API-toteutus on muuttunut.
API-dokumentaation kirjoittaminen Go-kommenttien muodossa voi auttaa ratkaisemaan tämän ongelman jossain määrin. Koska koodi ja dokumentaatio ovat samassa paikassa, kommentit voidaan päivittää samalla kun koodia muutetaan. On olemassa työkaluja, jotka automaattisesti luovat OpenAPI-spesifikaatiodokumentteja näiden kommenttien perusteella.
Tyypillinen esimerkki on Swag. Swag jäsentää Go-koodin kommentit ja luo OpenAPI 2 -muotoisen dokumentin. Käyttö on yksinkertaista. Kirjoita kommentit handler-funktion yläpuolelle kunkin kirjaston määrittelemässä muodossa.
1// @Summary Käyttäjän luominen
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än 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 tällä tavalla, Swag CLI jäsentää ne ja luo OpenAPI 2 -dokumentin. Yleensä tämä toimenpide suoritetaan CI-prosessin aikana, ja luotu OpenAPI-spesifikaatiodokumentti otetaan käyttöön Git-repositoriossa, lopullisessa koontituloksessa tai erillisessä ulkoisessa API-dokumenttien hallintajärjestelmässä, jotta sitä voidaan käyttää yhteistyössä muiden projektien kanssa.
Edut:
- Koska kommentit ovat koodin yhteydessä, todennäköisyys sille, että varsinainen koodi ja dokumentti ovat eri tilassa, vähenee.
- Dokumentaatio on helppoa ja vapaata pelkillä kommenteilla ilman erillisiä työkaluja tai monimutkaisia asetuksia.
- Koska kommentit eivät vaikuta varsinaiseen API-logiikkaan, on helppoa lisätä tilapäisiä toimintoja, joiden dokumentointia ei haluta julkistaa.
Haitat:
- Koska kommenttirivien määrä kasvaa, yksittäisen kooditiedoston luettavuus saattaa heikentyä.
- Kaikkien API-spesifikaatioiden ilmaiseminen kommenttimuodossa voi olla vaikeaa.
- Koska dokumentti ei pakota koodia, ei voida taata, että OpenAPI-dokumentti ja varsinainen logiikka ovat yhdenmukaisia.
2. Go-koodin luominen OpenAPI-spesifikaatiodokumentista
On olemassa myös tapa, jossa Single source of Truth (SSOT) ei ole Go-koodissa vaan dokumentissa. Tässä tapauksessa OpenAPI-spesifikaatio määritellään ensin, ja sen perusteella luodaan Go-koodi. Koska API-spesifikaatio luo koodia, se voi pakottaa kehityskulttuurissa API:n suunnittelun ensin. Koska API-spesifikaation määrittely on kehitysprosessin ensimmäinen vaihe, voidaan välttää ikävät tilanteet, joissa puutteet huomataan vasta kehityksen päätyttyä ja koko koodi joudutaan muuttamaan API-spesifikaation muutosten myötä.
Tätä lähestymistapaa käyttäviä tyypillisiä projekteja ovat oapi-codegen ja OpenAPI Generator. Käyttö on yksinkertaista.
- Kirjoita YAML- tai JSON-dokumentti OpenAPI-spesifikaation mukaisesti
- Suorita CLI
- Luodaan vastaava Go-stub-koodi.
- Nyt sinun tarvitsee vain toteuttaa yksityiskohtainen logiikka yksittäisiä API:ita varten, jotta stub voi käyttää niitä.
Seuraavassa on esimerkki oapi-codegenin luomasta koodista.
1// StrictServerInterface represents all server handlers.
2type StrictServerInterface interface {
3 // ...
4 // Returns all pets
5 // (GET /pets)
6 FindPets(ctx context.Context, request FindPetsRequestObject) (FindPetsResponseObject, error)
7 // ...
8}
Edellä mainitun rajapinnan avulla oapi-codegenin luoma koodi suorittaa kyselyparametrien, otsikoiden, rungon jäsentämisen ja validointilogiikan ja kutsuu rajapinnassa ilmoitettua sopivaa metodia. Käyttäjän tarvitsee vain toteuttaa yllä oleva rajapinta, jotta API-toteutukseen tarvittavat toimet saadaan suoritettua.
Edut:
- Koska spesifikaatio on määritelty ensin ja kehitys etenee sen jälkeen, on helpompaa suorittaa työtehtäviä rinnakkain, kun useat tiimit tekevät yhteistyötä.
- Koska toistuvat ja työläät tehtävät luodaan automaattisesti, työn tehokkuus paranee ja virheenkorjaus on edelleen helppoa.
- On helppoa varmistaa, että dokumentti ja koodi ovat aina yhdenmukaisia.
Haitat:
- Jos ei ole perehtynyt OpenAPI-spesifikaatioon, alkuunpano voi olla hieman vaikeaa.
- Koska API:ita käsittelevän koodin muodon luo projekti automaattisesti, mukauttaminen voi olla vaikeaa, jos se on tarpeen.
Kirjoittajan kommentti. Lokakuussa 2024 OpenAPI Generatorin luoma Go-koodi pakottaa koko projektin muodon API-logiikan lisäksi, ja projektin rakenne on jäykkä, joten se luo sopimattoman koodin erilaisten todelliseen tuotantoympäristöön tarvittavien toimintojen lisäämiseen. Niiden, jotka valitsevat tämän lähestymistavan, on suositeltavaa käyttää oapi-codegenia. Kirjoittaja käyttää oapi-codegen + echo + StrictServerInterfacea.
3. OpenAPI-spesifikaatiodokumentin luominen Go-koodista
Kun kymmenet tai sadat ihmiset kehittävät samaa palvelinta, on väistämätöntä, että yksittäisten API:iden yhtenäisyys saattaa vaarantua. Yksinkertaisena esimerkkinä, jos yli 100 API-päätepistettä määritellään yhdessä OpenAPI YAML -tiedostossa, tiedosto on yli 10 000 riviä pitkä hirviö, ja kun uusia API-päätepisteitä määritellään, on väistämätöntä, että samat mallit määritellään uudelleen tai joitakin kenttiä jätetään pois, tai syntyy polkunimikäytäntöjä, jotka eivät ole käytäntöjen mukaisia, jolloin API:n yleinen yhtenäisyys alkaa heikentyä.
Tämän ongelman ratkaisemiseksi voidaan määrittää erillinen omistaja OpenAPI YAML -tiedoston hallintaa varten tai kehittää linteri, joka havaitsee ongelmat automaattisesti CI-prosessin aikana, mutta Domain-specific language (DSL) voidaan määritellä Go-kielellä, jotta varmistetaan, että kaikki API:t ovat yhdenmukaisia.
Tyypillinen esimerkki tätä tekniikkaa käyttävästä projektista on Kubernetes (rakennettu sisäisesti ilman erillisiä kirjastoja), ja sitä voidaan käyttää projekteilla, kuten go-restful ja goa. Seuraavassa 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})
Kun kirjoitat yllä olevan tyyppisen käännettävän Go-koodin, saat samalla edun siitä, että POST /users
API:n toteutus ja dokumentin määrittely valmistuvat samanaikaisesti.
Edut:
- Koska kaikki tulee koodista, on helppoa säilyttää API:n yhtenäisyys koko projektissa.
- Hyödyntämällä Go:n vahvaa tyyppijärjestelmää voidaan saada tarkempi ja kiistattomampi spesifikaatio kuin käyttämällä kaikkia OpenAPI3:n ominaisuuksia.
Haitat:
- Kunkin kehyksen määrittelemä DSL on opittava, ja sen soveltaminen olemassa olevaan koodiin voi olla vaikeaa.
- Koska kehyksen ehdottamia sääntöjä on pakko noudattaa, vapaus ja joustavuus saattavat heikentyä.
Lopuksi
Kullakin menetelmällä on etuja ja haittoja, ja on tärkeää valita sopiva menetelmä projektin vaatimusten ja tiimin mieltymysten mukaan. Tärkeintä ei ole se, mikä lähestymistapa on paras, vaan sen arvioiminen, mikä ratkaisu sopii parhaiten nykyiseen tilanteeseen, ja kehityksen tuottavuuden parantaminen, jotta päästään aikaisin kotiin ja nautitaan tyytyväisestä työn ja vapaa-ajan tasapainosta.
Vaikka kirjoitan tätä lokakuussa 2024, Go- ja OpenAPI-ekosysteemit kehittyvät jatkuvasti, joten on suositeltavaa seurata jatkuvasti kunkin kirjaston ja projektin viimeisimpiä tapahtumia ja niiden muuttuneita etuja ja haittoja ottaen huomioon tämän artikkelin lukemisen ja kirjoittamisen välinen aika.
Onnellista Go-elämää~ 😘