Semua lapis sudah terpasang di Part 7: spec, generated interface, handler, service, repository, migration. Sekarang kita jalankan semuanya sungguhan, dari database kosong sampai response JSON yang keluar dari endpoint.
1. Migrate Database
$ make migrate-up
Running migrations...
2026/10/09 03:14:24 OK 00001_create_users_table.sql (12.73ms)
2026/10/09 03:14:24 OK 00002_create_products_table.sql (5.17ms)
goose: successfully migrated database to version: 2
Migration 00002_create_products_table.sql dari Part 7 jalan tanpa masalah di atas migration users yang sudah ada sebelumnya.
2. Nyalakan Server
$ go run cmd/http/main.go
┌───────────────────────────────────────────────────┐
│ gohexaclean │
│ Fiber v2.52.9 │
│ http://127.0.0.1:8080 │
│ (bound on host 0.0.0.0 and port 8080) │
│ │
│ Handlers ............ 25 Processes ........... 1 │
│ Prefork ....... Disabled PID ............. 96377 │
└───────────────────────────────────────────────────┘
25 handler terdaftar — gabungan dari healthapi, userapi, dan productapi yang baru ditambahkan di Part 6-7, semuanya hasil RegisterHandlers otomatis dari masing-masing spec OpenAPI.
3. Uji Semua Endpoint
Berikut hasil sungguhan dari server yang jalan di atas, melawan PostgreSQL asli (bukan mock atau in-memory):
Create:
$ curl -X POST localhost:8080/api/v1/products \
-H "Content-Type: application/json" \
-d '{"name":"Kaos IndoKoding","price":99000,"stock":50}'
{"data":{"created_at":"2026-10-09T03:15:01.566734+07:00","id":"5ae2d96c-f50b-4162-a23c-3d8dc7972b7b","name":"Kaos IndoKoding","price":99000,"stock":50,"updated_at":"2026-10-09T03:15:01.566734+07:00"},"message":"Product created successfully","success":true}
Get by ID:
$ curl localhost:8080/api/v1/products/5ae2d96c-f50b-4162-a23c-3d8dc7972b7b
{"data":{"created_at":"2026-10-09T03:15:01.566734+07:00","id":"5ae2d96c-f50b-4162-a23c-3d8dc7972b7b","name":"Kaos IndoKoding","price":99000,"stock":50,"updated_at":"2026-10-09T03:15:01.566734+07:00"},"message":"Product retrieved successfully","success":true}
List (dengan pagination meta):
$ curl "localhost:8080/api/v1/products?page=1&limit=10"
{"data":[{"created_at":"2026-10-09T03:15:01.566734+07:00","id":"5ae2d96c-f50b-4162-a23c-3d8dc7972b7b","name":"Kaos IndoKoding","price":99000,"stock":50,"updated_at":"2026-10-09T03:15:01.566734+07:00"}],"message":"Products retrieved successfully","meta":{"limit":10,"page":1,"total":1},"success":true}
Update:
$ curl -X PUT localhost:8080/api/v1/products/5ae2d96c-f50b-4162-a23c-3d8dc7972b7b \
-H "Content-Type: application/json" \
-d '{"name":"Kaos IndoKoding Edisi Baru","price":109000,"stock":40}'
{"data":{"created_at":"2026-10-09T03:15:01.566734+07:00","id":"5ae2d96c-f50b-4162-a23c-3d8dc7972b7b","name":"Kaos IndoKoding Edisi Baru","price":109000,"stock":40,"updated_at":"2026-10-09T03:15:01.566734+07:00"},"message":"Product updated successfully","success":true}
404 untuk ID yang tidak ada:
$ curl -o /dev/null -w "%{http_code}\n" localhost:8080/api/v1/products/00000000-0000-0000-0000-000000000000
404
Delete, lalu Get lagi (membuktikan soft-delete GORM benar-benar membuat record tidak lagi ditemukan):
$ curl -X DELETE localhost:8080/api/v1/products/5ae2d96c-f50b-4162-a23c-3d8dc7972b7b
{"message":"Product deleted successfully","success":true}
$ curl -o /dev/null -w "%{http_code}\n" localhost:8080/api/v1/products/5ae2d96c-f50b-4162-a23c-3d8dc7972b7b
404
Semua lima operation (listProducts, createProduct, getProductById, updateProduct, deleteProduct) yang didefinisikan di product-api.yaml sejak Part 6 terbukti berfungsi persis sesuai kontraknya — sampai ke perilaku soft-delete GORM yang membuat record "menghilang" dari FindByID setelah dihapus.
4. Pastikan Tidak Ada yang Rusak
Sebelum dianggap selesai, seluruh test suite yang sudah ada di boilerplate (untuk fitur User, yang tidak disentuh sama sekali di seri ini) dijalankan ulang untuk memastikan penambahan Product tidak merusak apa pun:
$ go vet ./...
$ go test ./...
ok .../internal/adapter/inbound/http/handler/user 1.057s
ok .../internal/adapter/outbound/pgsql 0.540s
ok .../internal/app 1.899s
go vet bersih tanpa output, dan semua test yang sudah ada sebelumnya tetap lulus.
Menutup Seri
Dari Part 1 sampai di sini, kita sudah menempuh satu siklus penuh spec-first dua kali — sekali di project standalone (Notes API), sekali lagi di project hexagonal architecture nyata (Product API):
- Tulis kontrak — OpenAPI YAML sebagai satu sumber kebenaran (Part 1-2)
- Pilih tools — oapi-codegen untuk pendekatan spec-first (Part 3)
- Generate — types dan
ServerInterfaceotomatis dari spec, tidak ditulis manual (Part 4, Part 7) - Implementasikan — handler meng-implement interface yang digenerate, dijaga compiler lewat
var _ Interface = (*Handler)(nil)(Part 4, Part 7) - Validasi — lint spec sebelum generate, supaya kesalahan ketahuan di awal (Part 5, Part 6)
- Dokumentasikan — Swagger UI yang selalu sinkron dengan kode karena sumbernya sama (Part 5)
- Terapkan ke arsitektur nyata — generated interface sebagai port inbound di hexagonal architecture (Part 6-8)
Pola yang sama ini bisa dipakai untuk menambahkan API baru apa pun ke project hexagonal architecture: tulis spec, make openapi, implementasikan ServerInterface-nya, sambungkan lewat container dan router. Tidak ada lagi dokumentasi API yang basi, karena dokumentasi dan kode lahir dari file yang sama.
Terima kasih sudah mengikuti seri ini sampai selesai. Selamat menulis spec dulu, baru kode.
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