Swagger zu Ihrer Go-API hinzufügen

OpenAPI-Dokumentation automatisch aus Code-Anmerkungen generieren

Inhaltsverzeichnis

API-Dokumentation ist entscheidend für jede moderne Anwendung, und für Go APIs Swagger (OpenAPI) hat sich zum Branchenstandard entwickelt. Für Go-Entwickler bietet swaggo eine elegante Lösung, um umfassende API-Dokumentation direkt aus Code-Anmerkungen zu generieren.

Swagger-API-Spezifikationen auf Agile-Board Dieses schöne Bild wurde von AI-Modell Flux 1 dev generiert.

Warum Swagger für Go-APIs wichtig ist

Beim Erstellen von REST-APIs wird die Dokumentation oft veraltet, wenn sich der Code weiterentwickelt. Swagger löst dieses Problem, indem es die Dokumentation aus dem Quellcode generiert und so sicherstellt, dass sie mit der Implementierung synchron bleibt. Die interaktive Swagger-Benutzeroberfläche ermöglicht es Entwicklern, Endpunkte direkt im Browser zu testen, was die Entwicklererfahrung erheblich verbessert.

Für Teams, die Microservices oder öffentliche APIs entwickeln, wird Swagger-Dokumentation zu einem wesentlichen Element für:

  • Client-Generierung: Automatische Erstellung von Client-Bibliotheken in mehreren Sprachen
  • Vertragstests: Validierung von Anfragen und Antworten gegen definierte Schemas
  • Teamzusammenarbeit: Bereitstellung einer einzigen Wissensquelle für API-Verträge
  • Einführung neuer Entwickler: Neue Teammitglieder können APIs interaktiv erkunden

Einstieg in swaggo

Die swaggo-Bibliothek ist das beliebteste Tool zur Hinzufügung von Swagger-Unterstützung zu Go-Anwendungen. Sie funktioniert, indem sie spezielle Kommentare im Code analysiert und OpenAPI 3.0-Spezifikationsdateien generiert.

Installation

Installieren Sie zunächst das swag-CLI-Tool:

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

Fügen Sie dann das entsprechende Swagger-Middleware-Paket für Ihr Framework hinzu. 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

Grundlegende Konfiguration

Beginnen Sie mit der Hinzufügung allgemeiner API-Informationen in Ihrer main.go-Datei. Ähnlich wie bei der Strukturierung einer REST-API in Go, sollten die Annotationen klar und beschreibend sein:

// @title           Produkt-API
// @version         1.0
// @description     Eine Produktverwaltungs-API mit 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 Geben Sie "Bearer" gefolgt von einem Leerzeichen und JWT-Token ein.

func main() {
    // Ihr Anwendungscode
}

Implementierung mit dem Gin-Framework

Lassen Sie uns ein vollständiges Beispiel mit Gin implementieren. Definieren Sie zunächst Ihre Datenmodelle mit Struct-Tags:

type Product struct {
    ID          int     `json:"id" example:"1"`
    Name        string  `json:"name" example:"Laptop" binding:"required"`
    Description string  `json:"description" example:"Hochleistungs-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:"Ungültige Eingabe"`
    Message string `json:"message" example:"Produktname ist erforderlich"`
}

Annotieren Sie nun Ihre Handler-Funktionen. Bei der Arbeit mit Datenbankoperationen, helfen diese Annotationen bei der Dokumentation des Datenflusses:

// GetProduct godoc
// @Summary      Produkt nach ID abrufen
// @Description  Abrufen eines einzelnen Produkts anhand seiner eindeutigen Kennung
// @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")
    // Implementierung hier
    c.JSON(200, Product{ID: 1, Name: "Laptop", Price: 999.99})
}

// CreateProduct godoc
// @Summary      Erstellen eines neuen Produkts
// @Description  Hinzufügen eines neuen Produkts zum Katalog
// @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
    }
    // Speichern in der Datenbank
    c.JSON(201, product)
}

Generieren von Dokumentation

Nach dem Annotieren Ihres Codes generieren Sie die Swagger-Dokumentation:

swag init

Dies erstellt einen docs-Ordner mit swagger.json, swagger.yaml und Go-Dateien. Importieren und registrieren Sie den Swagger-Endpunkt:

package main

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

    _ "yourproject/docs" // Importierte generierte Dokumente
)

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

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

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

    r.Run(":8080")
}

Greifen Sie nun auf Ihre interaktive API-Dokumentation zu unter http://localhost:8080/swagger/index.html.

Implementierung mit dem Echo-Framework

Echo-Nutzer folgen einem ähnlichen Muster, jedoch mit Echo-spezifischem Middleware:

package main

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

    _ "yourproject/docs"
)

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

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

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

    e.Start(":8080")
}

Implementierung mit dem Fiber-Framework

Die Implementierung von Fiber ist ebenso einfach:

package main

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

    _ "yourproject/docs"
)

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

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

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

    app.Listen(":8080")
}

Fortgeschrittene Swagger-Annotationen

Dokumentation komplexer Anforderungsbodies

Für verschachtelte Strukturen oder Arrays:

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      Erstellen einer neuen Bestellung
// @Description  Erstellen einer Bestellung mit mehreren Artikeln und Versandinformationen
// @Tags         orders
// @Accept       json
// @Produce      json
// @Param        order  body      CreateOrderRequest  true  "Bestelldetails"
// @Success      201    {object}  Order
// @Failure      400    {object}  ErrorResponse
// @Failure      422    {object}  ErrorResponse
// @Security     Bearer
// @Router       /orders [post]
func CreateOrder(c *gin.Context) {
    // Implementierung
}

Dokumentation von Datei-Uploads

// UploadImage godoc
// @Summary      Hochladen eines Produktbildes
// @Description  Hochladen einer Bilddatei für ein Produkt
// @Tags         products
// @Accept       multipart/form-data
// @Produce      json
// @Param        id    path      int   true  "Produkt-ID"
// @Param        file  formData  file  true  "Bilddatei"
// @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")
    // Upload verarbeiten
}

Abfrageparameter und Paginierung

// ListProducts godoc
// @Summary      Auflisten von Produkten mit Paginierung
// @Description  Abrufen einer paginierten Liste von Produkten mit optionaler Filterung
// @Tags         products
// @Accept       json
// @Produce      json
// @Param        page      query     int     false  "Seitenzahl" default(1)
// @Param        page_size query     int     false  "Elemente pro Seite" default(10)
// @Param        category  query     string  false  "Filter nach Kategorie"
// @Param        min_price query     number  false  "Mindestpreis"
// @Param        max_price query     number  false  "Höchstpreis"
// @Success      200       {array}   Product
// @Failure      400       {object}  ErrorResponse
// @Router       /products [get]
func ListProducts(c *gin.Context) {
    // Implementierung mit Paginierung
}

Authentifizierung und Sicherheit

Dokumentieren Sie verschiedene Authentifizierungsmethoden in Ihrer API. Für Multi-Tenant-Anwendungen, ist eine korrekte Authentifizierungsdokumentation entscheidend:

Bearer-Token-Authentifizierung

// @securityDefinitions.apikey Bearer
// @in header
// @name Authorization
// @description Geben Sie "Bearer" gefolgt von einem Leerzeichen und JWT-Token ein.

API-Key-Authentifizierung

// @securityDefinitions.apikey ApiKeyAuth
// @in header
// @name X-API-Key
// @description API-Key für die Authentifizierung

OAuth2-Authentifizierung

// @securitydefinitions.oauth2.application OAuth2Application
// @tokenUrl https://example.com/oauth/token
// @scope.write Gewährt Schreibzugriff
// @scope.admin Gewährt Lese- und Schreibzugriff auf administrative Informationen

Basic-Authentifizierung

// @securityDefinitions.basic BasicAuth

Wenden Sie Sicherheit auf bestimmte Endpunkte an:

// @Security Bearer
// @Security ApiKeyAuth

Anpassung der Swagger-Benutzeroberfläche

Sie können das Aussehen und Verhalten der Swagger-Benutzeroberfläche anpassen:

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

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

Um Swagger in der Produktion zu deaktivieren:

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

Integration mit CI/CD

Automatisieren Sie die Swagger-Dokumentationsgenerierung in Ihrer CI/CD-Pipeline:

# GitHub Actions Beispiel
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          

Best Practices

1. Konsistenter Annotationsstil

Halten Sie eine konsistente Formatierung für alle Endpunkte aufrecht:

// HandlerName godoc
// @Summary      Kurzbeschreibung (unter 50 Zeichen)
// @Description  Detaillierte Beschreibung, was der Endpunkt macht
// @Tags         ressourcen-name
// @Accept       json
// @Produce      json
// @Param        name  location  type  required  "Beschreibung"
// @Success      200   {object}  ResponseType
// @Failure      400   {object}  ErrorResponse
// @Router       /path [method]

2. Verwenden Sie beschreibende Beispiele

Fügen Sie realistische Beispiele hinzu, um API-Nutzern zu helfen:

type User struct {
    ID        int       `json:"id" example:"1"`
    Email     string    `json:"email" example:"benutzer@example.com"`
    CreatedAt time.Time `json:"created_at" example:"2025-01-15T10:30:00Z"`
}

3. Dokumentieren Sie alle Antwortcodes

Beinhalten Sie alle möglichen HTTP-Statuscodes:

// @Success      200  {object}  Product
// @Success      201  {object}  Product
// @Failure      400  {object}  ErrorResponse "Bad Request"
// @Failure      401  {object}  ErrorResponse "Unauthorized"
// @Failure      403  {object}  ErrorResponse "Forbidden"
// @Failure      404  {object}  ErrorResponse "Not Found"
// @Failure      422  {object}  ErrorResponse "Validation Error"
// @Failure      500  {object}  ErrorResponse "Internal Server Error"

4. Versionieren Sie Ihre API

Verwenden Sie eine korrekte Versionierung im Basis-Pfad:

// @BasePath  /api/v1

Und organisieren Sie Ihren Code entsprechend:

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

5. Gruppieren Sie verwandte Endpunkte

Verwenden Sie Tags, um Endpunkte logisch zu organisieren:

// @Tags products
// @Tags orders
// @Tags users

6. Halten Sie die Dokumentation aktuell

Führen Sie swag init vor jedem Commit aus oder integrieren Sie es in Ihren Build-Prozess:

#!/bin/bash
# Pre-commit Hook
swag init
git add docs/

Testen der Swagger-Dokumentation

Bei der Arbeit mit serverlosen Architekturen wie AWS Lambda wird das Testen Ihrer API-Dokumentation noch wichtiger:

func TestSwaggerGeneration(t *testing.T) {
    // Überprüfen, ob swagger.json existiert
    _, err := os.Stat("./docs/swagger.json")
    if err != nil {
        t.Fatal("swagger.json nicht gefunden, führen Sie 'swag init' aus")
    }

    // Überprüfen, ob der Swagger-Endpunkt antwortet
    r := setupRouter()
    w := httptest.NewRecorder()
    req, _ := http.NewRequest("GET", "/swagger/index.html", nil)
    r.ServeHTTP(w, req)

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

Häufige Probleme und Lösungen

Dokumentation wird nicht aktualisiert

Wenn Änderungen nicht erscheinen, stellen Sie sicher, dass Sie die Dokumente neu generieren:

swag init --parseDependency --parseInternal

Die --parseDependency-Flagge parst externe Abhängigkeiten und --parseInternal parst interne Pakete.

Benutzerdefinierte Typen werden nicht erkannt

Für Typen aus externen Paketen verwenden Sie den swaggertype-Tag:

type CustomTime struct {
    time.Time
}

func (CustomTime) SwaggerDoc() map[string]string {
    return map[string]string{
        "time": "RFC3339-Zeitstempel",
    }
}

Oder verwenden Sie den swaggertype-Tag:

type Product struct {
    ID        int        `json:"id"`
    UpdatedAt CustomTime `json:"updated_at" swaggertype:"string" format:"date-time"`
}

Arrays und Enums

Dokumentieren Sie Array-Typen und Enums:

type Filter struct {
    Status []string `json:"status" enums:"active,inactive,pending"`
    Tags   []string `json:"tags"`
}

// @Param  status  query  string  false  "Statusfilter" Enums(active, inactive, pending)

Alternative Ansätze

Während swaggo die beliebteste Wahl ist, gibt es andere Optionen:

go-swagger

Eine funktionsreichere, aber komplexere Alternative:

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

Manuelle OpenAPI-Dateien

Für vollständige Kontrolle schreiben Sie OpenAPI-Spezifikationen manuell in YAML:

openapi: 3.0.0
info:
  title: Product API
  version: 1.0.0
paths:
  /products:
    get:
      summary: List products
      responses:
        '200':
          description: Success

Dann servieren Sie mit:

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

Integration mit KI und LLMs

Beim Erstellen von APIs, die mit KI-Diensten integriert sind, wird eine ordnungsgemäße Dokumentation entscheidend. Zum Beispiel, wenn Sie mit strukturierten LLM-Ausgaben arbeiten, hilft Swagger bei der Dokumentation komplexer Anfrage- und Antwortschemata:

type LLMRequest struct {
    Prompt      string            `json:"prompt" example:"Fassen Sie diesen Text zusammen"`
    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      Generate structured LLM output
// @Description  Generate text with constrained output schema
// @Tags         llm
// @Accept       json
// @Produce      json
// @Param        request  body      LLMRequest  true  "LLM parameters"
// @Success      200      {object}  map[string]interface{}
// @Failure      400      {object}  ErrorResponse
// @Router       /llm/generate [post]
func GenerateStructured(c *gin.Context) {
    // Implementation
}

Leistungsüberlegungen

Swagger-Dokumentation hat minimalen Einfluss auf die Leistung:

  • Build-Zeit: swag init dauert 1-3 Sekunden für die meisten Projekte
  • Laufzeit: Die Dokumentation wird einmal beim Start geladen
  • Speicher: Fügt typischerweise 1-2MB zur Binärdateigröße hinzu
  • Antwortzeit: Kein Einfluss auf die API-Endpunkte selbst

Für sehr große APIs (100+ Endpunkte) sollten Sie in Betracht ziehen:

  • Aufteilung in mehrere Swagger-Dateien
  • Lazy-Loading von Swagger-UI-Assets
  • Servieren der Dokumentation von einem separaten Dienst

Sicherheitsüberlegungen

Beim Freigeben von Swagger-Dokumentation:

  1. Deaktivieren Sie in der Produktion (wenn die API intern ist):
if os.Getenv("ENV") == "production" {
    // Registrieren Sie keinen Swagger-Endpunkt
    return
}
  1. Fügen Sie Authentifizierung hinzu:
authorized := r.Group("/swagger")
authorized.Use(AuthMiddleware())
authorized.GET("/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
  1. Begrenzen Sie die Rate des Endpunkts:
r.GET("/swagger/*any", RateLimitMiddleware(), ginSwagger.WrapHandler(swaggerFiles.Handler))
  1. Geben Sie niemals interne Details preis:
  • Dokumentieren Sie keine internen Endpunkte
  • Vermeiden Sie die direkte Freigabe von Datenbankschemata
  • Bereinigen Sie Fehlermeldungen in der Dokumentation

Fazit

Die Hinzufügung von Swagger-Dokumentation zu Ihrer Go-API verwandelt die Entwicklererfahrung von Raten in geführte Erkundung. Die swaggo-Bibliothek macht diesen Prozess einfach, indem sie umfassende OpenAPI-Dokumentation aus Ihren Code-Anmerkungen generiert.

Wichtige Erkenntnisse:

  • Beginnen Sie mit grundlegenden Anmerkungen und erweitern Sie diese schrittweise
  • Halten Sie die Dokumentation durch CI/CD mit dem Code synchronisiert
  • Verwenden Sie Swagger UI für interaktives Testen während der Entwicklung
  • Dokumentieren Sie Authentifizierung, Fehler und Randfälle gründlich
  • Berücksichtigen Sie die Sicherheitsimplikationen bei der Freigabe der Dokumentation

Ob Sie Microservices, öffentliche APIs oder interne Tools erstellen, Swagger-Dokumentation zahlt sich aus in reduzierter Supportbelastung, schnellerem Onboarding und besserer API-Gestaltung. Die anfängliche Investition in das Lernen der Annotation-Syntax wird schnell zur Routine, und die automatische Generierung stellt sicher, dass Ihre Dokumentation nie hinter Ihrer Implementierung zurückbleibt.

Für Go-Entwickler schafft die Kombination aus starkem Typing, Code-Generierung und dem Annotation-System von swaggo einen leistungsfähigen Workflow, der API-Dokumentation zu einem natürlichen Teil des Entwicklungsprozesses macht, anstatt eine Nacharbeit zu sein.

Externe Ressourcen