M
Mr Sugiarto
Developer
16 Oct 2026 4 min read

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
  • info tanpa license — 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.

M
Mr Sugiarto

Developer

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