Notes API dari Part 4 sudah jalan. Di part penutup Bagian 1 ini, kita tutup dengan dua hal: memvalidasi spec secara otomatis (supaya kesalahan ketahuan sebelum di-generate jadi kode), dan menyajikan dokumentasi interaktif yang bisa dicoba langsung dari browser.
Kenapa Perlu Linter
YAML yang valid secara sintaks belum tentu spec OpenAPI yang baik. Contoh masalah yang sering lolos tanpa linter:
- Operation tanpa
security— ambigu, apakah memang publik atau cuma lupa ditulis? - Response sukses ada, tapi tidak ada satu pun response error (4xx) — padahal di dunia nyata request bisa saja invalid
infotanpalicense— penting untuk API publik supaya pemakai tahu syarat penggunaannya
Menjalankan Redocly CLI
npx @redocly/cli lint api/openapi/notes-api.yaml
Draft awal notes-api.yaml (sebelum diperbaiki) sempat menghasilkan output seperti ini:
[1] api/openapi/notes-api.yaml:19:5 at #/paths/~1notes/get
Every operation should have security defined on it or on the root level.
Error was generated by the security-defined rule.
Error yang sama muncul lima kali (satu untuk tiap operation: listNotes, createNote, getNote, updateNote, deleteNote), ditambah beberapa warning: info tanpa license, server localhost (wajar untuk dev, tapi tetap diingatkan), tag tanpa description, dan GET /notes tanpa response 4xx.
Memperbaikinya
Error security-defined diperbaiki dengan mendeklarasikan eksplisit di root level bahwa API ini publik (bukan lupa ditulis):
security: []
Warning lainnya diperbaiki satu-satu: tambah license di info, tambah description di tag, tambah response 400 untuk GET /notes. Semuanya sudah ada di versi final notes-api.yaml yang dibahas di Part 2.
Hasil akhirnya:
$ npx @redocly/cli lint api/openapi/notes-api.yaml
api/openapi/notes-api.yaml: validated in 19ms
Woohoo! Your API description is valid. 🎉
You have 1 warning.
Satu warning yang tersisa (server mengarah ke localhost) memang disengaja — ini server development, bukan production, jadi warning itu valid dan dibiarkan.
Lint ini juga langsung dijalankan ulang ke product-api.yaml di Bagian 2 nanti (Part 6), dengan hasil yang sama bersihnya — jadi kebiasaan "lint sebelum generate" ini bukan langkah sekali pakai, tapi bagian dari siklus kerja spec-first.
Serve Dokumentasi Interaktif
Linter menjaga kualitas spec-nya. Sekarang kita buat spec itu bisa dicoba langsung dari browser, tanpa perlu curl atau Postman — pakai Swagger UI.
Generated code dari oapi-codegen (Part 4) sudah menyediakan fungsi untuk mengambil spec dalam bentuk JSON di runtime, lewat flag -generate spec yang sudah kita pakai:
func GetSpecJSON() ([]byte, error) {
rawSpec, err := decodeSpecCached()()
// ...
}
Dua route baru ditambahkan di cmd/main.go: satu untuk serve spec sebagai JSON, satu untuk serve halaman Swagger UI yang menunjuk ke spec itu.
app.Get("/openapi.json", func(c *fiber.Ctx) error {
spec, err := notesapi.GetSpecJSON()
if err != nil {
return c.Status(fiber.StatusInternalServerError).SendString(err.Error())
}
c.Set("Content-Type", "application/json")
return c.Send(spec)
})
app.Get("/docs", func(c *fiber.Ctx) error {
c.Set("Content-Type", "text/html")
return c.SendString(swaggerUIPage)
})
swaggerUIPage adalah halaman HTML statis yang memuat Swagger UI dari CDN dan mengarahkannya ke /openapi.json:
const swaggerUIPage = `<!DOCTYPE html>
<html>
<head>
<title>Notes API - Docs</title>
<link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5.33.1/swagger-ui.css"
integrity="sha384-Ov4/wv3j2bmct8cDc5X4ngJZohVPzEmc6uDPH8WeljUxO5vtoykvMEfbu9Vh6RaW" crossorigin="anonymous">
</head>
<body>
<div id="swagger-ui"></div>
<script src="https://unpkg.com/swagger-ui-dist@5.33.1/swagger-ui-bundle.js"
integrity="sha384-ZPehFMQommnnuaZ4rpxgkgTT2DKFVp4hZC/7pLit+9Lek9T1YGSo23eHFbvNkXkw" crossorigin="anonymous"></script>
<script>
window.onload = () => {
window.ui = SwaggerUIBundle({
url: '/openapi.json',
dom_id: '#swagger-ui',
});
};
</script>
</body>
</html>`
Perhatikan atribut integrity dan crossorigin di kedua tag — ini Subresource Integrity, memastikan file yang dimuat dari CDN benar-benar sama dengan yang di-hash saat ditulis, bukan versi yang sudah dimodifikasi (misalnya kalau CDN-nya suatu saat disusupi).
Hasil Sungguhan
$ go run ./cmd/main.go
$ curl -s -D - -o /dev/null localhost:8080/openapi.json | grep -i content-type
Content-Type: application/json
$ curl -s -o /dev/null -w "%{http_code}\n" localhost:8080/docs
200
/openapi.json benar-benar mengembalikan spec yang sama dengan notes-api.yaml, sudah dikonversi ke JSON dan di-embed ke dalam binary saat generate — jadi server tidak perlu baca file YAML di disk saat runtime. Buka localhost:8080/docs di browser, dan kelima endpoint Notes API bisa dicoba langsung dari situ.
Penutup Bagian 1
Sampai di sini, Notes API sudah lengkap satu siklus penuh spec-first: tulis YAML (Part 2) → pilih tools (Part 3) → generate dan implementasikan (Part 4) → validasi dan dokumentasikan (Part 5). Semua berjalan di project yang berdiri sendiri, tanpa bergantung pada arsitektur atau framework tertentu.
Di Bagian 2 (Part 6-8), kita pindah ke project yang lebih dekat dengan kondisi nyata: boilerplate Go dengan hexagonal architecture, menambahkan fitur baru (Product API) dari nol memakai workflow spec-first yang sama persis.
Bagian dari Series: OpenAPI Spec-First untuk Go Developer
Seri 8 bagian: belajar mendesain REST API dengan pendekatan spec-first di Go - menulis kontrak OpenAPI dulu, generate kode, lalu terapkan ke project h...
Lihat Series Lengkap