Menambahkan Swagger ke API Go Anda

Buat dokumen OpenAPI secara otomatis dari anotasi kode

Konten Halaman

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.

swagger api specs on agile board 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 init mengambil 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:

  1. Nonaktifkan di Produksi (jika API internal):
if os.Getenv("ENV") == "production" {
    // Jangan daftarkan endpoint Swagger
    return
}
  1. Tambahkan Otentikasi:
authorized := r.Group("/swagger")
authorized.Use(AuthMiddleware())
authorized.GET("/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
  1. Batasi Laju Endpoint:
r.GET("/swagger/*any", RateLimitMiddleware(), ginSwagger.WrapHandler(swaggerFiles.Handler))
  1. 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

Sumber Daya Eksternal