Lägg till Swagger i Din Go API

Generera OpenAPI-dokument automatiskt från kodkommentarer

Sidinnehåll

API-dokumentation är avgörande för någon modern applikation, och för Go APIs Swagger (OpenAPI) har blivit branschstandard. För Go-utvecklare erbjuder swaggo en elegant lösning för att generera omfattande API-dokumentation direkt från kodkommentarer.

swagger api specs på agile board Det här fina bilden är genererad av AI-modellen Flux 1 dev.

Varför Swagger är viktigt för Go-API:er

När man bygger REST-API:er blir dokumentationen ofta föråldrad när koden utvecklas. Swagger löser detta genom att generera dokumentation från din källkod, vilket säkerställer att den förblir synkroniserad med din implementation. Den interaktiva Swagger UI låter utvecklare testa slutpunkter direkt från webbläsaren, vilket betydligt förbättrar utvecklarupplevelsen.

För team som bygger mikrotjänster eller offentliga API:er blir Swagger-dokumentation avgörande för:

  • Klientgenerering: Automatiskt skapa klientbibliotek på flera språk
  • Kontraktstestning: Validera begäranden och svar mot definierade scheman
  • Teamarbete: Erbjuda en enda sanningskälla för API-kontrakt
  • Utvecklarintroduktion: Nya teammedlemmar kan utforska API:er interaktivt

Kom igång med swaggo

Swaggo-biblioteket är det mest populära verktyget för att lägga till Swagger-stöd till Go-applikationer. Det fungerar genom att tolka specialkommentarer i din kod och generera OpenAPI 3.0-specifikationsfiler.

Installation

Installera först swag CLI-verktyget:

go install github.com/swaggo/swag/cmd/swag@latest

Lägg sedan till den lämpliga Swagger-mellanhandsprogramspaketet för din ramverk. För Gin:

go get -u github.com/swaggo/gin-swagger
go get -u github.com/swaggo/files

För Echo:

go get -u github.com/swaggo/echo-swagger

För Fiber:

go get -u github.com/gofiber/swagger

Grundläggande konfiguration

Börja med att lägga till allmän API-information i din main.go fil. Liknande hur du skulle strukturera en REST API i Go, bör kommentarerna vara klara och beskrivande:

// @title           Produkt API
// @version         1.0
// @description     En produktförvaltnings-API med Swagger-dokumentation
// @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 Skriv "Bearer" följt av ett mellanslag och JWT-token.

func main() {
    // Din applikationskod
}

Implementering med Gin-ramverk

Låt oss implementera ett komplett exempel med Gin. Börja med att definiera dina datamodeller med struct-taggar:

type Product struct {
    ID          int     `json:"id" example:"1"`
    Name        string  `json:"name" example:"Laptop" binding:"required"`
    Description string  `json:"description" example:"Högpresterande laptop"`
    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:"Ogiltig inmatning"`
    Message string `json:"message" example:"Produktnamn är obligatoriskt"`
}

Kommentera nu dina hanteringsfunktioner. När du arbetar med databashantering, hjälper dessa kommentarer att dokumentera dataflödet:

// GetProduct godoc
// @Summary      Hämta produkt efter ID
// @Description  Hämta en enda produkt efter dess unika identifierare
// @Tags         products
// @Accept       json
// @Produce      json
// @Param        id   path      int  true  "Produkt ID"
// @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")
    // Implementering här
    c.JSON(200, Product{ID: 1, Name: "Laptop", Price: 999.99})
}

// CreateProduct godoc
// @Summary      Skapa en ny produkt
// @Description  Lägg till en ny produkt i katalogen
// @Tags         products
// @Accept       json
// @Produce      json
// @Param        product  body      Product  true  "Produkt objekt"
// @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: "Bad Request", Message: err.Error()})
        return
    }
    // Spara till databas
    c.JSON(201, product)
}

Generera dokumentation

Efter att ha kommenterat din kod, generera Swagger-dokumentationen:

swag init

Detta skapar en docs mapp med swagger.json, swagger.yaml och Go-filer. Importera och registrera Swagger-slutpunkten:

package main

import (
    "github.com/gin-gonic/gin"
    swaggerFiles "github.com/swaggo/files"
    ginSwagger "github.com/swaggo/gin-swagger"

    _ "yourproject/docs" // Importera genererade dokument
)

func main() {
    r := gin.Default()

    // API-rutter
    v1 := r.Group("/api/v1")
    {
        v1.GET("/products/:id", GetProduct)
        v1.POST("/products", CreateProduct)
    }

    // Swagger-slutpunkt
    r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))

    r.Run(":8080")
}

Nu kan du komma åt din interaktiva API-dokumentation på http://localhost:8080/swagger/index.html.

Implementering med Echo-ramverk

Echo-användare följer ett liknande mönster men med Echo-specifikt mellanhandsprogram:

package main

import (
    "github.com/labstack/echo/v4"
    echoSwagger "github.com/swaggo/echo-swagger"

    _ "yourproject/docs"
)

func main() {
    e := echo.New()

    // API-rutter
    api := e.Group("/api/v1")
    api.GET("/products/:id", getProduct)
    api.POST("/products", createProduct)

    // Swagger-slutpunkt
    e.GET("/swagger/*", echoSwagger.WrapHandler)

    e.Start(":8080")
}

Implementering med Fiber-ramverk

Fibers implementering är lika rak:

package main

import (
    "github.com/gofiber/fiber/v2"
    "github.com/gofiber/swagger"

    _ "yourproject/docs"
)

func main() {
    app := fiber.New()

    // API-rutter
    api := app.Group("/api/v1")
    api.Get("/products/:id", getProduct)
    api.Post("/products", createProduct)

    // Swagger-slutpunkt
    app.Get("/swagger/*", swagger.HandlerDefault)

    app.Listen(":8080")
}

Avancerade Swagger-kommentarer

Dokumentera komplexa begärande kroppar

För inbäddade strukturer eller arrayer:

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      Skapa en ny order
// @Description  Skapa en order med flera artiklar och leveransinformation
// @Tags         orders
// @Accept       json
// @Produce      json
// @Param        order  body      CreateOrderRequest  true  "Order detaljer"
// @Success      201    {object}  Order
// @Failure      400    {object}  ErrorResponse
// @Failure      422    {object}  ErrorResponse
// @Security     Bearer
// @Router       /orders [post]
func CreateOrder(c *gin.Context) {
    // Implementering
}

Dokumentera filuppladdningar

// UploadImage godoc
// @Summary      Ladda upp produktbild
// @Description  Ladda upp en bildfil för en produkt
// @Tags         products
// @Accept       multipart/form-data
// @Produce      json
// @Param        id    path      int   true  "Produkt ID"
// @Param        file  formData  file  true  "Bildfil"
// @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")
    // Hantera uppladdning
}

Sökparametrar och sidnumrering

// ListProducts godoc
// @Summary      Lista produkter med sidnumrering
// @Description  Hämta sidnummerad lista över produkter med valfri filtrering
// @Tags         products
// @Accept       json
// @Produce      json
// @Param        page      query     int     false  "Sidanummer" default(1)
// @Param        page_size query     int     false  "Antal per sida" default(10)
// @Param        category  query     string  false  "Filtrera efter kategori"
// @Param        min_price query     number  false  "Minsta pris"
// @Param        max_price query     number  false  "Maximalt pris"
// @Success      200       {array}   Product
// @Failure      400       {object}  ErrorResponse
// @Router       /products [get]
func ListProducts(c *gin.Context) {
    // Implementering med sidnumrering
}

Autentisering och säkerhet

Dokumentera olika autentiseringsmetoder i ditt API. För multi-tenant-applikationer, är korrekt autentiseringsdokumentation avgörande:

Bearer Token-autentisering

// @securityDefinitions.apikey Bearer
// @in header
// @name Authorization
// @description Skriv "Bearer" följt av ett mellanslag och JWT-token.

API-nyckel autentisering

// @securityDefinitions.apikey ApiKeyAuth
// @in header
// @name X-API-Key
// @description API-nyckel för autentisering

OAuth2-autentisering

// @securitydefinitions.oauth2.application OAuth2Application
// @tokenUrl https://example.com/oauth/token
// @scope.write Ger skrivrättigheter
// @scope.admin Ger läsa och skrivrättigheter till administrativ information

Basic-autentisering

// @securityDefinitions.basic BasicAuth

Tillämpa säkerhet på specifika slutpunkter:

// @Security Bearer
// @Security ApiKeyAuth

Anpassa Swagger UI

Du kan anpassa Swagger UI:s utseende och beteende:

// Anpassad konfiguration
url := ginSwagger.URL("http://localhost:8080/swagger/doc.json")
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler, url))

// Med anpassad titel
r.GET("/swagger/*any", ginSwagger.WrapHandler(
    swaggerFiles.Handler,
    ginSwagger.URL("http://localhost:8080/swagger/doc.json"),
    ginSwagger.DefaultModelsExpandDepth(-1),
))

För att inaktivera Swagger i produktion:

if os.Getenv("ENV") != "production" {
    r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
}

Integrering med CI/CD

Automatisera Swagger-dokumentationsgenerering i din CI/CD-pipeline:

# GitHub Actions exempel
name: Generera Swagger-dokumentation
on: [push]

jobs:
  swagger:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Konfigurera Go
        uses: actions/setup-go@v4
        with:
          go-version: '1.21'

      - name: Installera swag
        run: go install github.com/swaggo/swag/cmd/swag@latest

      - name: Generera Swagger-dokumentation
        run: swag init

      - name: Committa dokumentation
        run: |
          git config user.name github-actions
          git config user.email github-actions@github.com
          git add docs/
          git commit -m "Uppdatera Swagger-dokumentation" || exit 0
          git push          

Bästa praxis

1. Enhetlig kommenteringsstil

Håll enhetlig formatering över alla slutpunkter:

// HandlerName godoc
// @Summary      Kort beskrivning (under 50 tecken)
// @Description  Detaljerad beskrivning av vad slutpunkten gör
// @Tags         resursnamn
// @Accept       json
// @Produce      json
// @Param        namn  plats  typ  obligatorisk  "beskrivning"
// @Success      200   {object}  Svarstyp
// @Failure      400   {object}  FelSvar
// @Router       /sökväg [metod]

2. Använd beskrivande exempel

Lägg till realistiska exempel för att hjälpa API-användare:

type Användare struct {
    ID        int       `json:"id" example:"1"`
    Email     string    `json:"email" example:"användare@example.com"`
    Skapad   time.Time `json:"skapad" example:"2025-01-15T10:30:00Z"`
}

3. Dokumentera alla svarskoder

Inkludera alla möjliga HTTP-statuskoder:

// @Success      200  {object}  Produkt
// @Success      201  {object}  Produkt
// @Failure      400  {object}  FelSvar "Ogiltig begäran"
// @Failure      401  {object}  FelSvar "Ej auktoriserad"
// @Failure      403  {object}  FelSvar "Förbjuden"
// @Failure      404  {object}  FelSvar "Hittades inte"
// @Failure      422  {object}  FelSvar "Valideringsfel"
// @Failure      500  {object}  FelSvar "Internt serverfel"

4. Versionshantera ditt API

Använd korrekt versionshantering i basväg:

// @BasePath  /api/v1

Och organisera din kod därefter:

v1 := r.Group("/api/v1")
v2 := r.Group("/api/v2")

5. Gruppera relaterade slutpunkter

Använd taggar för att logiskt organisera slutpunkter:

// @Tags produkter
// @Tags beställningar
// @Tags användare

6. Håll dokumentationen uppdaterad

Kör swag init innan varje commit eller integrera det i din byggprocess:

#!/bin/bash
# pre-commit-hook
swag init
git add docs/

Testning av Swagger-dokumentation

När du arbetar med serverlösa arkitekturer som AWS Lambda blir det ännu viktigare att testa din API-dokumentation:

func TestSwaggerGenerering(t *testing.T) {
    // Verifiera att swagger.json finns
    _, err := os.Stat("./docs/swagger.json")
    if err != nil {
        t.Fatal("swagger.json hittades inte, kör 'swag init'")
    }

    // Verifiera att swagger-slutpunkten svarar
    r := setupRouter()
    w := httptest.NewRecorder()
    req, _ := http.NewRequest("GET", "/swagger/index.html", nil)
    r.ServeHTTP(w, req)

    assert.Equal(t, 200, w.Code)
}

Vanliga problem och lösningar

Dokumentation uppdateras inte

Om ändringar inte visas, se till att du återskapar dokumentationen:

swag init --parseDependency --parseInternal

Flaggan --parseDependency parser externa beroenden, och --parseInternal parser interna paket.

Anpassade typer erkänns inte

För typer från externa paket, använd swaggertype-taggen:

type AnpassadTid struct {
    time.Time
}

func (AnpassadTid) SwaggerDoc() map[string]string {
    return map[string]string{
        "tid": "RFC3339-tidsstämpel",
    }
}

Eller använd swaggertype-taggen:

type Produkt struct {
    ID        int        `json:"id"`
    Uppdaterad AnpassadTid `json:"uppdaterad" swaggertype:"string" format:"date-time"`
}

Arrayer och enumereringar

Dokumentera arraytyper och enumereringar:

type Filter struct {
    Status []string `json:"status" enums:"aktiv,inaktiv,väntande"`
    Taggar []string `json:"taggar"`
}

// @Param  status  query  string  false  "Statusfilter" Enums(aktiv, inaktiv, väntande)

Alternativa tillvägagångssätt

Medan swaggo är det mest populära valet finns andra alternativ:

go-swagger

Ett mer funktionellt men komplext alternativ:

brew install go-swagger
swagger generate spec -o ./swagger.json

Manuella OpenAPI-filer

För full kontroll, skriv OpenAPI-specifikationer manuellt i YAML:

openapi: 3.0.0
info:
  title: Produkt API
  version: 1.0.0
paths:
  /produkter:
    get:
      summary: Lista produkter
      responses:
        '200':
          description: Lyckades

Därefter servera med:

r.StaticFile("/openapi.yaml", "./openapi.yaml")

Integrering med AI och LLMs

När du bygger API:er som integreras med AI-tjänster blir korrekt dokumentation avgörande. Till exempel, när du arbetar med strukturerade LLM-utdata, hjälper Swagger dokumentera komplexa begärans- och svarscheman:

type LLMFörfrågan struct {
    Prompt      string            `json:"prompt" example:"Sammanfatta denna text"`
    Modell      string            `json:"modell" example:"qwen2.5:latest"`
    Temperatur  float64           `json:"temperatur" example:"0.7" minimum:"0" maximum:"2"`
    MaxTokens   int               `json:"max_tokens" example:"1000" minimum:"1"`
    Schema      map[string]interface{} `json:"schema,omitempty"`
}

// GenereraStrukturerad godoc
// @Summary      Generera strukturerad LLM-utdata
// @Description  Generera text med begränsad utdata-schema
// @Tags         llm
// @Accept       json
// @Produce      json
// @Param        förfrågan  body      LLMFörfrågan  true  "LLM-parametrar"
// @Success      200      {object}  map[string]interface{}
// @Failure      400      {object}  FelSvar
// @Router       /llm/generera [post]
func GenereraStrukturerad(c *gin.Context) {
    // Implementering
}

Prestandaöverväganden

Swagger-dokumentation har minimal prestandapåverkan:

  • Byggtid: swag init tar 1-3 sekunder för de flesta projekt
  • Körtid: Dokumentationen laddas en gång vid start
  • Minne: Lägger till 1-2MB till binärstorleken
  • Svarstid: Ingen påverkan på API-slutpunkterna själva

För mycket stora API:er (100+ slutpunkter), överväg:

  • Att dela upp i flera Swagger-filer
  • Att lata ladda Swagger UI-assets
  • Att servera dokumentationen från en separat tjänst

Säkerhetsöverväganden

När du exponerar Swagger-dokumentation:

  1. Inaktivera i produktion (om API är internt):
if os.Getenv("MILJÖ") == "produktion" {
    // Registrera inte Swagger-slutpunkten
    return
}
  1. Lägg till autentisering:
auktoriserad := r.Group("/swagger")
auktoriserad.Use(AuthMiddleware())
auktoriserad.GET("/*allt", ginSwagger.WrapHandler(swaggerFiles.Handler))
  1. Begränsa hastigheten på slutpunkten:
r.GET("/swagger/*allt", RateLimitMiddleware(), ginSwagger.WrapHandler(swaggerFiles.Handler))
  1. Exponera aldrig interna detaljer:
  • Dokumentera inte interna slutpunkter
  • Undvik att exponera databasscheman direkt
  • Sanitera felmeddelanden i dokumentationen

Slutsats

Att lägga till Swagger-dokumentation till ditt Go-API förvandlar utvecklarupplevelsen från gissande till vägledd utforskning. Swaggo-biblioteket gör denna process enkel genom att generera omfattande OpenAPI-dokumentation från dina kodkommentarer.

Viktiga slutsatser:

  • Börja med grundläggande kommentarer och utöka gradvis
  • Håll dokumentationen synkroniserad med koden genom CI/CD
  • Använd Swagger UI för interaktiv testning under utveckling
  • Dokumentera autentisering, fel och gränssituationer grundligt
  • Överväg säkerhetsimplikationerna när du exponerar dokumentationen

Oavsett om du bygger mikrotjänster, offentliga API:er eller interna verktyg, betalar Swagger-dokumentation av sig i minskad supportbelastning, snabbare introduktion och bättre API-design. Den initiala investeringen i att lära sig kommenteringssyntax blir snart rutin, och automatisk generering säkerställer att din dokumentation aldrig hamnar efter din implementering.

För Go-utvecklare skapar kombinationen av stark typning, kodgenerering och swaggos kommenteringssystem ett kraftfullt arbetsflöde som gör API-dokumentation till en naturlig del av utvecklingsprocessen istället för en eftertanke.

Användbara länkar

Externa resurser