GoSuda

Ekosistem Go dan OpenAPI

By iwanhae views ...

Pendahuluan

Saat mengembangkan server Production Backend dengan bahasa Go, hampir semua pengembang pasti menghadapi salah satu tantangan pertama berikut ini.

Dokumentasi API, bagaimana caranya...?

Setelah sedikit mencari informasi, kita akan menyadari bahwa menulis dokumen yang sesuai dengan spesifikasi OpenAPI itu bermanfaat, dan secara alami kita akan mencari pustaka yang terintegrasi dengan OpenAPI. Namun, meskipun keputusan ini sudah diambil, masih ada masalah berikutnya.

Pustaka terkait OpenAPI itu banyak sekali.. mana yang sebaiknya dipakai...?

Dokumen ini adalah pengantar singkat tentang pustaka yang ditulis untuk para pemula Go yang sedang mengalami situasi tersebut. Dokumen ini ditulis berdasarkan kondisi akhir tahun 2024, dan karena ekosistem bahasa selalu berubah secara dinamis, disarankan untuk tetap memeriksa dan mengikuti perkembangan terbaru sambil membaca dokumen ini.

Strategi Pustaka dalam Menangani OpenAPI

Sebagian besar Anda mungkin sudah mengetahuinya, tetapi OpenAPI adalah spesifikasi untuk mendefinisikan dan mendokumentasikan REST API secara jelas. Dengan mendefinisikan endpoint API, request, dan format response dalam format YAML atau JSON, spesifikasi ini tidak hanya membantu pengembang, tetapi juga membantu mengotomatisasi pembuatan kode di sisi frontend maupun backend, sehingga pekerjaan yang berulang tidak perlu dilakukan dan kesalahan manusia yang kecil dapat dikurangi secara signifikan.

Untuk mengintegrasikan OpenAPI ke dalam proyek secara alami, pustaka-pustaka di ekosistem Go secara garis besar mengambil tiga strategi berikut.

1. Menyusun Spesifikasi Dokumen OpenAPI dari Komentar Go

Salah satu kesulitan dalam mengembangkan API yang sesuai dengan OpenAPI adalah bahwa dokumen sebenarnya dan kode yang mengimplementasikannya berada di file terpisah dan lokasi yang berbeda. Akibatnya, cukup sering terjadi situasi di mana kode diperbarui tetapi dokumen tidak, atau dokumen diperbarui tetapi kode tidak.

Sebagai contoh sederhana:

  1. Logika API diubah di dalam file ./internal/server/user.go
  2. Dokumen yang sebenarnya berada di ./openapi3.yaml, dan perubahan pada file tersebut bisa saja terlewat secara tidak sengaja.
  3. Jika perubahan tersebut tidak disadari dan Pull Request diajukan lalu ditinjau oleh rekan kerja,
  4. Karena peninjau juga tidak dapat melihat perubahan pada ./openapi3.yaml, dapat terjadi kejadian tidak diinginkan di mana spesifikasi API tetap sama, tetapi implementasi API sudah berubah.

Dengan menulis dokumentasi API dalam bentuk komentar Go, masalah tersebut dapat dikurangi sampai batas tertentu. Karena kode dan dokumen berada di satu tempat, komentar dapat diperbarui bersamaan dengan saat kode diubah. Terdapat sejumlah alat yang secara otomatis menghasilkan dokumen spesifikasi OpenAPI berdasarkan komentar-komentar tersebut.

Contoh proyek yang representatif adalah Swag. Swag mem-parsing komentar pada kode Go dan menghasilkan dokumen dalam format OpenAPI 2. Cara penggunaannya cukup sederhana: cukup tuliskan komentar di atas fungsi handler sesuai format yang ditetapkan oleh pustaka tersebut.

 1// @Summary 유저 생성
 2// @Description 새로운 유저를 생성합니다.
 3// @Tags Users
 4// @Accept json
 5// @Produce json
 6// @Param user body models.User true "유저 정보"
 7// @Success 200 {object} models.User
 8// @Failure 400 {object} models.ErrorResponse
 9// @Router /users [post]
10func CreateUser(c *gin.Context) {
11    // ...
12}

Ketika komentar ditulis seperti ini, CLI Swag akan mem-parsing komentar tersebut dan menghasilkan dokumen OpenAPI 2. Biasanya proses ini dijalankan dalam tahap CI, dan dokumen spesifikasi OpenAPI yang dihasilkan kemudian didistribusikan ke Git Repository, hasil build akhir, atau sistem manajemen dokumen API eksternal, untuk digunakan dalam kolaborasi dengan proyek lain.

Kelebihan:

  • Karena komentar berada bersama kode, kemungkinan bentuk kode dan dokumen berbeda menjadi lebih kecil.
  • Dokumentasi dapat dibuat dengan mudah dan fleksibel hanya dengan komentar, tanpa alat tambahan atau konfigurasi yang rumit.
  • Karena komentar tidak memengaruhi logika API yang sebenarnya, sangat mudah untuk menambahkan fitur sementara yang terasa berat jika didokumentasikan secara publik.

Kekurangan:

  • Jumlah baris komentar yang bertambah dapat menurunkan keterbacaan sebuah file kode.
  • Mungkin sulit untuk merepresentasikan seluruh spesifikasi API hanya dalam bentuk komentar.
  • Dokumen tidak memaksa kode, sehingga tidak ada jaminan bahwa dokumen OpenAPI dan logika aktual selalu sesuai.

2. Menghasilkan Kode Go dari Dokumen Spesifikasi OpenAPI

Ada juga cara untuk menempatkan Single Source of Truth (SSOT) bukan pada kode Go, melainkan pada dokumen. Yaitu dengan mendefinisikan spesifikasi OpenAPI terlebih dahulu, lalu menghasilkan kode Go berdasarkan definisi tersebut. Karena spesifikasi API itu sendiri menghasilkan kode, budaya pengembangan dapat mendorong perancangan API dilakukan terlebih dahulu, dan karena definisi spesifikasi API menjadi tahap pertama dalam alur pengembangan, keunggulannya adalah dapat mencegah sejak dini situasi tidak diinginkan di mana bagian yang terlewat baru disadari setelah pengembangan selesai, sehingga seluruh kode harus diubah bersamaan dengan perubahan spesifikasi API.

Contoh proyek yang menerapkan pendekatan ini antara lain oapi-codegen dan OpenAPI Generator. Cara penggunaannya juga sederhana.

  1. Tulis dokumen yaml atau json sesuai spesifikasi OpenAPI
  2. Jalankan CLI
  3. Kode stub Go yang sesuai akan dihasilkan
  4. Anda hanya perlu mengimplementasikan logika detail untuk setiap API agar stub tersebut dapat digunakan

Berikut adalah contoh kode yang dihasilkan oleh oapi-codegen.

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}

Kode yang dihasilkan oleh oapi-codegen berdasarkan interface tersebut memiliki struktur yang melakukan parsing query parameters, header, dan body, serta validasi, lalu memanggil method yang sesuai yang dideklarasikan pada interface. Pengguna cukup mengimplementasikan konkret dari interface tersebut agar pekerjaan implementasi API selesai.

Kelebihan:

  • Karena spesifikasi dibuat lebih dahulu lalu pengembangan dilanjutkan, pendekatan ini memudahkan pekerjaan paralel ketika beberapa tim berkolaborasi.
  • Kode untuk bagian yang sebelumnya dikerjakan secara berulang dan membosankan dihasilkan secara otomatis, sehingga produktivitas kerja meningkat sekaligus tetap menguntungkan untuk proses debugging.
  • Mudah menjamin bahwa bentuk dokumen dan kode selalu sesuai.

Kekurangan:

  • Jika belum memahami spesifikasi OpenAPI itu sendiri, kurva belajar awal cukup curam.
  • Karena bentuk kode untuk menangani API dihasilkan secara otomatis oleh proyek, bisa sulit ditangani jika diperlukan kustomisasi.

Komentar penulis. Per Oktober 2024, kode Go yang dihasilkan oleh OpenAPI Generator tidak hanya memaksakan logika API, tetapi juga seluruh struktur proyek, dan karena strukturnya kaku, kode yang dihasilkan kurang cocok untuk menambahkan berbagai fitur yang dibutuhkan di lingkungan Production. Bagi yang memilih pendekatan ini, sangat disarankan untuk menggunakan oapi-codegen. Penulis menggunakan oapi-codegen + echo + StrictServerInterface.

3. Menghasilkan Dokumen Spesifikasi OpenAPI dari Kode Go

Ketika puluhan hingga ratusan orang mengembangkan layanan pada server yang sama, masalah yang pasti muncul adalah konsistensi setiap API dapat mulai rusak. Sebagai contoh yang mudah dipahami, jika spesifikasi lebih dari 100 endpoint API dideklarasikan dalam satu file OpenAPI yaml, file tersebut akan menjadi monster dengan lebih dari 10.000 baris. Ketika endpoint API baru dideklarasikan, model yang sama secara tidak terelakkan bisa dideklarasikan berulang kali, beberapa field bisa terlewat, atau penamaan path yang tidak sesuai konvensi bisa muncul, sehingga konsistensi API secara keseluruhan mulai rusak.

Untuk mengatasi masalah ini, dapat dilakukan dengan menunjuk Owner khusus untuk mengelola OpenAPI yaml, atau mengembangkan Linter yang dijalankan dalam proses CI untuk mendeteksi masalah secara otomatis. Namun, dengan Go, kita juga dapat mendefinisikan Domain-specific language (DSL) sehingga semua API dipaksa memiliki konsistensi yang seragam.

Contoh proyek yang menerapkan teknik ini secara representatif adalah Kubernetes (dibangun sendiri tanpa pustaka tambahan), dan Anda juga dapat mencoba proyek seperti go-restful dan goa. Berikut adalah contoh penggunaan 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})

Dengan menulis kode Go yang dapat dikompilasi seperti di atas, implementasi API POST /users dan definisi dokumennya dapat diselesaikan secara bersamaan, yang merupakan keunggulan utamanya.

Kelebihan:

  • Karena semuanya berasal dari kode, konsistensi API di seluruh proyek lebih mudah dipertahankan.
  • Dengan memanfaatkan sistem tipe statis Go, dapat diperoleh spesifikasi yang lebih akurat dan tidak menimbulkan perdebatan dibandingkan ketika seluruh fitur OpenAPI3 digunakan.

Kekurangan:

  • Perlu mempelajari DSL yang didefinisikan oleh masing-masing framework, dan mungkin sulit diterapkan pada kode yang sudah ada.
  • Karena harus mengikuti aturan yang diusulkan framework, kebebasan dan fleksibilitas dapat berkurang.

Penutup

Setiap metode memiliki kelebihan dan kekurangan masing-masing, dan penting untuk memilih metode yang sesuai dengan kebutuhan proyek dan preferensi tim. Yang paling penting bukanlah metode mana yang lebih baik, melainkan melakukan penilaian terhadap solusi yang paling sesuai dengan situasi Anda saat ini, dan menjaga produktivitas pengembangan tetap tinggi agar Anda dapat menikmati pulang lebih cepat dan keseimbangan hidup kerja yang memuaskan.

Meskipun artikel ini ditulis berdasarkan kondisi Oktober 2024, ekosistem Go dan OpenAPI terus berkembang. Oleh karena itu, mengingat jarak waktu antara saat Anda membaca tulisan ini dan saat ditulis, harap terus mengikuti perkembangan terbaru serta perubahan kelebihan dan kekurangan dari masing-masing pustaka dan proyek.

Semoga Anda menikmati kehidupan Go yang menyenangkan~ 😘