Menambahkan Swagger ke API Go Anda
Buat dokumen OpenAPI secara otomatis dari anotasi kode
Dokumentasi API sangat penting untuk setiap aplikasi modern, dan untuk Go APIs Swagger (OpenAPI) telah menjadi standar industri.
Bagi pengembang Go, swaggo menyediakan solusi yang elegan untuk menghasilkan dokumentasi API yang komprehensif secara langsung dari anotasi kode.
Gambar yang menarik ini dihasilkan oleh AI model Flux 1 dev.
Mengapa Swagger Penting untuk Go APIs
Ketika membangun API REST, dokumentasi seringkali menjadi usang seiring berubahnya kode. Swagger mengatasi masalah ini dengan menghasilkan dokumentasi dari kode sumber Anda, memastikan bahwa dokumentasi tetap selaras dengan implementasi Anda. Tampilan interaktif Swagger UI memungkinkan pengembang menguji endpoint langsung dari browser, secara signifikan meningkatkan pengalaman pengembang.
Untuk tim yang membangun microservices atau API publik, dokumentasi Swagger menjadi penting untuk:
- Penghasilan Klien: Membuat perpustakaan klien secara otomatis dalam berbagai bahasa
- Uji Kontrak: Memvalidasi permintaan dan respons terhadap skema yang didefinisikan
- Kolaborasi Tim: Memberikan satu sumber kebenaran untuk kontrak API
- Onboarding Pengembang: Anggota baru tim dapat menjelajahi API secara interaktif
Memulai dengan swaggo
Perpustakaan swaggo adalah alat paling populer untuk menambahkan dukungan Swagger ke aplikasi Go. Ini bekerja dengan mem-parsing komentar khusus di kode Anda dan menghasilkan file spesifikasi OpenAPI 3.0.
Instalasi
Pertama, instal alat CLI swag:
go install github.com/swaggo/swag/cmd/swag@latest
Kemudian tambahkan paket middleware Swagger yang sesuai untuk framework Anda. Untuk Gin:
go get -u github.com/swaggo/gin-swagger
go get -u github.com/swaggo/files
Untuk Echo:
go get -u github.com/swaggo/echo-swagger
Untuk Fiber:
go get -u github.com/gofiber/swagger
Konfigurasi Dasar
Mulailah dengan menambahkan informasi API umum di file main.go Anda. Mirip dengan cara Anda akan mengatur REST API di Go, anotasi harus jelas dan deskriptif:
// @title Product API
// @version 1.0
// @description Sebuah API manajemen produk dengan dokumentasi Swagger
// @termsOfService http://swagger.io/terms/
// @contact.name API Support
// @contact.url http://www.swagger.io/support
// @contact.email support@swagger.io
// @license.name Apache 2.0
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html
// @host localhost:8080
// @BasePath /api/v1
// @securityDefinitions.apikey Bearer
// @in header
// @name Authorization
// @description Masukkan "Bearer" diikuti oleh spasi dan token JWT.
func main() {
// Kode aplikasi Anda
}
Implementasi dengan Framework Gin
Mari kita implementasikan contoh lengkap menggunakan Gin. Pertama, definisikan model data dengan tag struct:
type Product struct {
ID int `json:"id" example:"1"`
Name string `json:"name" example:"Laptop" binding:"required"`
Description string `json:"description" example:"Laptop berkinerja tinggi"`
Price float64 `json:"price" example:"999.99" binding:"required,gt=0"`
Stock int `json:"stock" example:"50"`
}
type ErrorResponse struct {
Error string `json:"error" example:"Input tidak valid"`
Message string `json:"message" example:"Nama produk diperlukan"`
}
Sekarang anotasi fungsi penangan Anda. Saat bekerja dengan operasi database, anotasi ini membantu mendokumentasikan alur data:
// GetProduct godoc
// @Summary Dapatkan produk berdasarkan ID
// @Description Dapatkan satu produk berdasarkan identifikasi uniknya
// @Tags produk
// @Accept json
// @Produce json
// @Param id path int true "ID Produk"
// @Success 200 {object} Product
// @Failure 400 {object} ErrorResponse
// @Failure 404 {object} ErrorResponse
// @Router /products/{id} [get]
func GetProduct(c *gin.Context) {
id := c.Param("id")
// Implementasi di sini
c.JSON(200, Product{ID: 1, Name: "Laptop", Price: 999.99})
}
// CreateProduct godoc
// @Summary Membuat produk baru
// @Description Tambahkan produk baru ke katalog
// @Tags produk
// @Accept json
// @Produce json
// @Param product body Product true "Objek produk"
// @Success 201 {object} Product
// @Failure 400 {object} ErrorResponse
// @Security Bearer
// @Router /products [post]
func CreateProduct(c *gin.Context) {
var product Product
if err := c.ShouldBindJSON(&product); err != nil {
c.JSON(400, ErrorResponse{Error: "Permintaan Buruk", Message: err.Error()})
return
}
// Simpan ke database
c.JSON(201, product)
}
Menghasilkan Dokumentasi
Setelah mengannotasi kode Anda, buat dokumentasi Swagger:
swag init
Ini membuat folder docs dengan swagger.json, swagger.yaml, dan file Go. Impor dan daftarkan endpoint Swagger:
package main
import (
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
_ "yourproject/docs" // Impor dokumen yang dihasilkan
)
func main() {
r := gin.Default()
// Rute API
v1 := r.Group("/api/v1")
{
v1.GET("/products/:id", GetProduct)
v1.POST("/products", CreateProduct)
}
// Endpoint Swagger
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
r.Run(":8080")
}
Sekarang akses dokumentasi API interaktif Anda di http://localhost:8080/swagger/index.html.
Implementasi dengan Framework Echo
Pengguna Echo mengikuti pola serupa tetapi dengan middleware khusus Echo:
package main
import (
"github.com/labstack/echo/v4"
echoSwagger "github.com/swaggo/echo-swagger"
_ "yourproject/docs"
)
func main() {
e := echo.New()
// Rute API
api := e.Group("/api/v1")
api.GET("/products/:id", getProduct)
api.POST("/products", createProduct)
// Endpoint Swagger
e.GET("/swagger/*", echoSwagger.WrapHandler)
e.Start(":8080")
}
Implementasi dengan Framework Fiber
Implementasi Fiber sama mudahnya:
package main
import (
"github.com/gofiber/fiber/v2"
"github.com/gofiber/swagger"
_ "yourproject/docs"
)
func main() {
app := fiber.New()
// Rute API
api := app.Group("/api/v1")
api.Get("/products/:id", getProduct)
api.Post("/products", createProduct)
// Endpoint Swagger
app.Get("/swagger/*", swagger.HandlerDefault)
app.Listen(":8080")
}
Anotasi Swagger Lanjutan
Mendokumentasikan Tubuh Permintaan yang Kompleks
Untuk struktur bersarang atau array:
type CreateOrderRequest struct {
CustomerID int `json:"customer_id" example:"123" binding:"required"`
Items []OrderItem `json:"items" binding:"required,min=1"`
ShippingAddress Address `json:"shipping_address" binding:"required"`
}
type OrderItem struct {
ProductID int `json:"product_id" example:"1" binding:"required"`
Quantity int `json:"quantity" example:"2" binding:"required,min=1"`
}
type Address struct {
Street string `json:"street" example:"123 Main St" binding:"required"`
City string `json:"city" example:"New York" binding:"required"`
ZipCode string `json:"zip_code" example:"10001" binding:"required"`
}
// CreateOrder godoc
// @Summary Membuat pesanan baru
// @Description Membuat pesanan dengan beberapa item dan informasi pengiriman
// @Tags pesanan
// @Accept json
// @Produce json
// @Param order body CreateOrderRequest true "Detail pesanan"
// @Success 201 {object} Order
// @Failure 400 {object} ErrorResponse
// @Failure 422 {object} ErrorResponse
// @Security Bearer
// @Router /orders [post]
func CreateOrder(c *gin.Context) {
// Implementasi
}
Mendokumentasikan Unggah File
// UploadImage godoc
// @Summary Unggah gambar produk
// @Description Unggah file gambar untuk produk
// @Tags produk
// @Accept multipart/form-data
// @Produce json
// @Param id path int true "ID Produk"
// @Param file formData file true "File gambar"
// @Success 200 {object} map[string]string
// @Failure 400 {object} ErrorResponse
// @Security Bearer
// @Router /products/{id}/image [post]
func UploadImage(c *gin.Context) {
file, _ := c.FormFile("file")
// Handle unggah
}
Parameter Pencarian dan Paginasi
// ListProducts godoc
// @Summary Daftar produk dengan paginasi
// @Description Dapatkan daftar produk yang dipaginasi dengan penyaringan opsional
// @Tags produk
// @Accept json
// @Produce json
// @Param page query int false "Nomor halaman" default(1)
// @Param page_size query int false "Jumlah item per halaman" default(10)
// @Param category query string false "Penyaring berdasarkan kategori"
// @Param min_price query number false "Harga minimum"
// @Param max_price query number false "Harga maksimum"
// @Success 200 {array} Product
// @Failure 400 {object} ErrorResponse
// @Router /products [get]
func ListProducts(c *gin.Context) {
// Implementasi dengan paginasi
}
Otentikasi dan Keamanan
Dokumentasikan berbagai metode otentikasi dalam API Anda. Untuk aplikasi multi-tenant, dokumentasi otentikasi yang tepat sangat penting:
Otentikasi Token Bearer
// @securityDefinitions.apikey Bearer
// @in header
// @name Authorization
// @description Masukkan "Bearer" diikuti oleh spasi dan token JWT.
Otentikasi API Key
// @securityDefinitions.apikey ApiKeyAuth
// @in header
// @name X-API-Key
// @description Kunci API untuk otentikasi
Otentikasi OAuth2
// @securitydefinitions.oauth2.application OAuth2Application
// @tokenUrl https://example.com/oauth/token
// @scope.write Memberi izin akses tulis
// @scope.admin Memberi izin baca dan tulis terhadap informasi administratif
Otentikasi Dasar
// @securityDefinitions.basic BasicAuth
Terapkan keamanan ke endpoint tertentu:
// @Security Bearer
// @Security ApiKeyAuth
Menyesuaikan Tampilan UI Swagger
Anda dapat menyesuaikan penampilan dan perilaku UI Swagger:
// Konfigurasi kustom
url := ginSwagger.URL("http://localhost:8080/swagger/doc.json")
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler, url))
// Dengan judul kustom
r.GET("/swagger/*any", ginSwagger.WrapHandler(
swaggerFiles.Handler,
ginSwagger.URL("http://localhost:8080/swagger/doc.json"),
ginSwagger.DefaultModelsExpandDepth(-1),
))
Untuk menonaktifkan Swagger di produksi:
if os.Getenv("ENV") != "production" {
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
}
Mengintegrasikan dengan CI/CD
Otomatisasi pembuatan dokumentasi Swagger dalam pipeline CI/CD Anda:
# Contoh GitHub Actions
name: Generate Swagger Docs
on: [push]
jobs:
swagger:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.21'
- name: Install swag
run: go install github.com/swaggo/swag/cmd/swag@latest
- name: Generate Swagger docs
run: swag init
- name: Commit docs
run: |
git config user.name github-actions
git config user.email github-actions@github.com
git add docs/
git commit -m "Update Swagger documentation" || exit 0
git push
Praktik Terbaik
1. Gaya Anotasi yang Konsisten
Pertahankan format yang konsisten di semua endpoint:
// HandlerName godoc
// @Summary Deskripsi singkat (di bawah 50 karakter)
// @Description Deskripsi terperinci dari apa yang dilakukan endpoint
// @Tags nama-sumber-daya
// @Accept json
// @Produce json
// @Param nama lokasi tipe diperlukan "deskripsi"
// @Success 200 {object} TipeRespons
// @Failure 400 {object} ErrorResponse
// @Router /path [metode]
2. Gunakan Contoh yang Deskriptif
Tambahkan contoh yang realistis untuk membantu pengguna API:
type User struct {
ID int `json:"id" example:"1"`
Email string `json:"email" example:"user@example.com"`
CreatedAt time.Time `json:"created_at" example:"2025-01-15T10:30:00Z"`
}
3. Dokumentasikan Semua Kode Respons
Sertakan semua kode respons HTTP yang mungkin:
// @Success 200 {object} Product
// @Success 201 {object} Product
// @Failure 400 {object} ErrorResponse "Permintaan Buruk"
// @Failure 401 {object} ErrorResponse "Tidak Diizinkan"
// @Failure 403 {object} ErrorResponse "Dilarang"
// @Failure 404 {object} ErrorResponse "Tidak Ditemukan"
// @Failure 422 {object} ErrorResponse "Kesalahan Validasi"
// @Failure 500 {object} ErrorResponse "Kesalahan Server Internal"
4. Versi API Anda
Gunakan versi yang tepat dalam jalur dasar:
// @BasePath /api/v1
Dan organisasikan kode Anda sesuai:
v1 := r.Group("/api/v1")
v2 := r.Group("/api/v2")
5. Kelompokkan Endpoint yang Terkait
Gunakan tag untuk mengorganisasi endpoint secara logis:
// @Tags produk
// @Tags pesanan
// @Tags pengguna
6. Pertahankan Dokumentasi Terkini
Jalankan swag init sebelum setiap commit atau integrasikan ke dalam proses pembuatan Anda:
#!/bin/bash
# hook pre-commit
swag init
git add docs/
Pengujian Dokumentasi Swagger
Ketika bekerja dengan arsitektur serverless seperti AWS Lambda, pengujian dokumentasi API Anda menjadi lebih penting:
func TestSwaggerGeneration(t *testing.T) {
// Verifikasi swagger.json ada
_, err := os.Stat("./docs/swagger.json")
if err != nil {
t.Fatal("swagger.json tidak ditemukan, jalankan 'swag init'")
}
// Verifikasi endpoint swagger merespons
r := setupRouter()
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/swagger/index.html", nil)
r.ServeHTTP(w, req)
assert.Equal(t, 200, w.Code)
}
Masalah Umum dan Solusinya
Dokumentasi Tidak Diperbarui
Jika perubahan tidak muncul, pastikan Anda menghasilkan dokumen kembali:
swag init --parseDependency --parseInternal
Flag --parseDependency mem-parsing ketergantungan eksternal, dan --parseInternal mem-parsing paket internal.
Tipe Kustom Tidak Dikenali
Untuk tipe dari paket eksternal, gunakan tag swaggertype:
type CustomTime struct {
time.Time
}
func (CustomTime) SwaggerDoc() map[string]string {
return map[string]string{
"time": "Timestamp RFC3339",
}
}
Atau gunakan tag swaggertype:
type Product struct {
ID int `json:"id"`
UpdatedAt CustomTime `json:"updated_at" swaggertype:"string" format:"date-time"`
}
Array dan Enum
Dokumentasikan tipe array dan enum:
type Filter struct {
Status []string `json:"status" enums:"aktif,nonaktif,menggantung"`
Tags []string `json:"tags"`
}
// @Param status query string false "Penyaring status" Enums(aktif, nonaktif, menggantung)
Pendekatan Alternatif
Meskipun swaggo adalah pilihan yang paling populer, pilihan lain juga ada:
go-swagger
Alternatif yang lebih fitur lengkap tetapi kompleks:
brew install go-swagger
swagger generate spec -o ./swagger.json
File OpenAPI Manual
Untuk kontrol penuh, tulis spesifikasi OpenAPI secara manual dalam YAML:
openapi: 3.0.0
info:
title: Product API
version: 1.0.0
paths:
/products:
get:
summary: Daftar produk
responses:
'200':
description: Sukses
Kemudian layani dengan:
r.StaticFile("/openapi.yaml", "./openapi.yaml")
Mengintegrasikan dengan AI dan LLM
Ketika membangun API yang terintegrasi dengan layanan AI, dokumentasi yang tepat menjadi sangat penting. Misalnya, ketika bekerja dengan keluaran LLM terstruktur, Swagger membantu mendokumentasikan skema permintaan dan respons yang kompleks:
type LLMRequest struct {
Prompt string `json:"prompt" example:"Ringkas teks ini"`
Model string `json:"model" example:"qwen2.5:latest"`
Temperature float64 `json:"temperature" example:"0.7" minimum:"0" maximum:"2"`
MaxTokens int `json:"max_tokens" example:"1000" minimum:"1"`
Schema map[string]interface{} `json:"schema,omitempty"`
}
// GenerateStructured godoc
// @Summary Menghasilkan keluaran LLM terstruktur
// @Description Menghasilkan teks dengan skema output yang dibatasi
// @Tags llm
// @Accept json
// @Produce json
// @Param request body LLMRequest true "Parameter LLM"
// @Success 200 {object} map[string]interface{}
// @Failure 400 {object} ErrorResponse
// @Router /llm/generate [post]
func GenerateStructured(c *gin.Context) {
// Implementasi
}
Pertimbangan Kinerja
Dokumentasi Swagger memiliki dampak kinerja yang minimal:
- Waktu Bangun:
swag initmengambil 1-3 detik untuk sebagian besar proyek - Waktu Eksekusi: Dokumentasi dimuat sekali saat startup
- Memori: Biasanya menambahkan 1-2MB ke ukuran biner
- Waktu Respons: Tidak memengaruhi endpoint API itu sendiri
Untuk API besar (100+ endpoint), pertimbangkan:
- Membagi menjadi beberapa file Swagger
- Menggunakan Swagger UI secara malas
- Mengirimkan dokumentasi dari layanan terpisah
Pertimbangan Keamanan
Ketika menampilkan dokumentasi Swagger:
- Nonaktifkan di Produksi (jika API internal):
if os.Getenv("ENV") == "production" {
// Jangan daftarkan endpoint Swagger
return
}
- Tambahkan Otentikasi:
authorized := r.Group("/swagger")
authorized.Use(AuthMiddleware())
authorized.GET("/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
- Batasi Laju Endpoint:
r.GET("/swagger/*any", RateLimitMiddleware(), ginSwagger.WrapHandler(swaggerFiles.Handler))
- Jangan Pernah Menampilkan Detail Internal:
- Jangan mendokumentasikan endpoint internal
- Hindari menampilkan skema database secara langsung
- Bersihkan pesan kesalahan dalam dokumentasi
Kesimpulan
Menambahkan dokumentasi Swagger ke API Go Anda mengubah pengalaman pengembang dari tebakan menjadi eksplorasi yang terarah. Perpustakaan swaggo membuat proses ini sederhana dengan menghasilkan dokumentasi OpenAPI komprehensif dari anotasi kode Anda.
Poin penting:
- Mulailah dengan anotasi dasar dan perluas secara bertahap
- Pertahankan dokumentasi selaras dengan kode melalui CI/CD
- Gunakan Swagger UI untuk pengujian interaktif selama pengembangan
- Dokumentasikan otentikasi, kesalahan, dan kasus batas secara menyeluruh
- Pertimbangkan implikasi keamanan ketika menampilkan dokumentasi
Baik Anda sedang membangun microservices, API publik, atau alat internal, dokumentasi Swagger memberikan keuntungan dalam mengurangi beban dukungan, mempercepat onboarding, dan meningkatkan desain API. Investasi awal dalam mempelajari sintaks anotasi dengan cepat menjadi rutinitas, dan pembuatan otomatis memastikan dokumentasi Anda tidak tertinggal dari implementasi.
Untuk pengembang Go, kombinasi tipe kuat, pembuatan kode, dan sistem anotasi swaggo menciptakan alur kerja yang kuat yang membuat dokumentasi API menjadi bagian alami dari proses pengembangan, bukan sesuatu yang dilakukan setelahnya.
Tautan Berguna
- Go Cheatsheet
- Membangun REST API di Go
- Perbandingan ORMs Go untuk PostgreSQL: GORM vs Ent vs Bun vs sqlc
- Polanya Multi-Tenancy Database dengan contoh dalam Go
- LLMs dengan Output Terstruktur: Ollama, Qwen3 & Python atau Go
- Kinerja AWS Lambda: JavaScript vs Python vs Go