← Semua pembelajaran / Go Nol → Enterprise
Fase 7 · Framework Web & Frontend

OpenAPI — kontrak API yang dijaga compiler

Dokumentasi API yang ditulis terpisah selalu basi. Pendekatan spec-first membalikkannya: spesifikasi jadi sumber kebenaran, dan compiler yang memastikan kodemu mematuhinya.

Sumber asli pkg.go.dev Resmi Rangkuman ~6 menit baca

Intisari

  • Spec-first: tulis openapi.yaml → generate tipe + interface server → compiler menolak kalau ada endpoint yang tidak diimplementasikan.
  • Alternatifnya code-first (anotasi komentar) — lebih cepat dimulai, tapi dokumentasinya kembali bisa basi.
  • Klien untuk konsumen API bisa digenerate dalam banyak bahasa dari spesifikasi yang sama.
  • Validasi permintaan bisa dijalankan otomatis dari spesifikasi lewat middleware.
  • Untuk API internal antar tim, ini menghapus seluruh kategori kesalahpahaman kontrak.

Spesifikasinya

openapi: 3.0.3
info:
  title: Toko API
  version: 1.0.0

paths:
  /produk:
    get:
      operationId: daftarProduk
      parameters:
        - name: q
          in: query
          schema: { type: string, maxLength: 100 }
        - name: batas
          in: query
          schema: { type: integer, minimum: 1, maximum: 100, default: 20 }
      responses:
        "200":
          description: Daftar produk
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HalamanProduk"

    post:
      operationId: buatProduk
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BuatProdukReq"
      responses:
        "201": { description: Dibuat }
        "422": { $ref: "#/components/responses/Validasi" }

components:
  schemas:
    Produk:
      type: object
      required: [id, nama, harga]
      properties:
        id:    { type: integer, format: int64 }
        nama:  { type: string, minLength: 3, maxLength: 200 }
        harga: { type: integer, format: int64, minimum: 0 }

Menghasilkan kode

# oapi-codegen.yaml
package: api
output: internal/api/api.gen.go
generate:
  models: true
  chi-server: true
  strict-server: true
  embedded-spec: true
go get -tool github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen
go tool oapi-codegen -config oapi-codegen.yaml openapi.yaml
// internal/api/api.gen.go — DIGENERATE
type ServerInterface interface {
	DaftarProduk(w http.ResponseWriter, r *http.Request, params DaftarProdukParams)
	BuatProduk(w http.ResponseWriter, r *http.Request)
}

func HandlerFromMux(si ServerInterface, r chi.Router) http.Handler

Inilah bagian yang membuat pendekatan ini berbeda dari dokumentasi biasa. Karena ServerInterface digenerate dari spesifikasi, menambahkan endpoint di openapi.yaml membuat kodemu gagal kompilasi sampai kamu mengimplementasikannya. Dan mengubah bentuk respons di spesifikasi membuat setiap handler yang masih mengembalikan bentuk lama ikut gagal. Dokumentasi dan kode tidak bisa lagi berbeda.

Mengimplementasikannya

type Server struct {
	produk *produk.Layanan
	log    *slog.Logger
}

var _ api.ServerInterface = (*Server)(nil)   // pemeriksaan saat kompilasi (Fase 1)

func (s *Server) DaftarProduk(w http.ResponseWriter, r *http.Request,
	p api.DaftarProdukParams) {

	batas := 20
	if p.Batas != nil {
		batas = *p.Batas          // sudah divalidasi 1–100 oleh middleware spec
	}

	hasil, err := s.produk.Cari(r.Context(), deref(p.Q), batas)
	if err != nil {
		s.tulisError(w, r, err)
		return
	}
	tulisJSON(w, http.StatusOK, keHalaman(hasil))
}

Validasi otomatis dari spesifikasi

spec, _ := api.GetSwagger()
spec.Servers = nil                       // jangan validasi host

r := chi.NewRouter()
r.Use(nethttpmiddleware.OapiRequestValidator(spec))
h := api.HandlerFromMux(server, r)

Permintaan yang melanggar spesifikasi — field wajib hilang, tipe salah, batas=500 — ditolak sebelum menyentuh handler. Aturan validasinya ditulis sekali, di tempat yang juga jadi dokumentasi.

Spec-first versus code-first

Spec-first (oapi-codegen)Code-first (anotasi swaggo)
Sumber kebenaranopenapi.yamlKomentar di kode
Dokumentasi bisa basi?Tidak — compiler menjaganyaYa — komentar bisa lupa diperbarui
Bisa dirancang sebelum kodeYa — berguna untuk kesepakatan antar timTidak
Kecepatan memulaiLebih lambatLebih cepat
Klien untuk konsumenGenerate dari spesifikasiGenerate dari spesifikasi hasil ekspor
Cocok untukAPI publik, kontrak antar timAPI internal kecil

Untuk API yang dikonsumsi tim lain, spec-first hampir selalu terbayar. Spesifikasi bisa di-review sebagai diff, disepakati sebelum ada satu baris kode, dan dipakai tim frontend untuk menghasilkan klien serta tiruan (mock) sejak hari pertama — sebelum backend-nya selesai.

Memberi versi pada API

PerubahanAman?
Menambah field pada respons✅ selama klien mengabaikan yang tidak dikenal
Menambah field opsional pada permintaan✅
Menambah endpoint✅
Menghapus atau mengganti nama field❌ breaking
Memperketat validasi❌ breaking
Mengubah tipe field❌ breaking
Mengubah arti nilai yang sudah ada❌ breaking, dan yang paling sulit terdeteksi

Latihan: tulis openapi.yaml untuk dua endpoint, generate ServerInterface, dan pasang var _ api.ServerInterface = (*Server)(nil). Lalu tambahkan endpoint ketiga di spesifikasi, generate ulang, dan lihat go build menolak sampai kamu menuliskannya. Itu perbedaan antara dokumentasi dan kontrak.

Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.