Swagger toevoegen aan je Go API
OpenAPI-documentatie automatisch genereren op basis van codeannotaties
API-documentatie is essentieel voor elke moderne toepassing, en voor Go API’s Swagger (OpenAPI) is geworden de industrie-standaard. Voor Go-ontwikkelaars biedt swaggo een elegante oplossing om uitgebreide API-documentatie direct te genereren uit code-annotaties.
Deze mooie afbeelding wordt gegenereerd door AI model Flux 1 dev.
Waarom Swagger belangrijk is voor Go API’s
Bij het bouwen van REST API’s wordt documentatie vaak verouderd als de code evolueert. Swagger lost dit op door documentatie te genereren uit je broncode, waardoor het in synchronisatie blijft met je implementatie. Het interactieve Swagger UI laat ontwikkelaars eindpunten rechtstreeks vanuit de browser testen, wat het ontwikkelaarservaring aanzienlijk verbetert.
Voor teams die microservices of openbare API’s bouwen, wordt Swagger-documentatie essentieel voor:
- Clientgeneratie: Creëer automatisch clientbibliotheken in meerdere talen
- Contracttesten: Valideer aanvragen en reacties tegen gedefinieerde schema’s
- Teamcoördinatie: Geef een enkele bron van waarheid voor API-contracten
- Ontwikkelaarsopvang: Nieuwe teamleden kunnen API’s interactief verkennen
Aan de slag met swaggo
De swaggo-bibliotheek is de populairste tool om Swagger-ondersteuning toe te voegen aan Go-toepassingen. Het werkt door speciale commentaren in je code te parsen en OpenAPI 3.0-specifiekbestanden te genereren.
Installatie
Installeer eerst het swag CLI-hulpprogramma:
go install github.com/swaggo/swag/cmd/swag@latest
Voeg vervolgens de juiste Swagger-middlewarebibliotheek toe voor je framework. Voor Gin:
go get -u github.com/swaggo/gin-swagger
go get -u github.com/swaggo/files
Voor Echo:
go get -u github.com/swaggo/echo-swagger
Voor Fiber:
go get -u github.com/gofiber/swagger
Basisconfiguratie
Begin met het toevoegen van algemene API-informatie in je main.go-bestand. Net zoals je zou doen bij het structureren van een REST API in Go, moeten de annotaties duidelijk en beschrijvend zijn:
// @title Product API
// @version 1.0
// @description Een productbeheer API met Swagger-documentatie
// @termsOfService http://swagger.io/terms/
// @contact.name API Ondersteuning
// @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 Type "Bearer" gevolgd door een spatie en JWT-token.
func main() {
// Je applicatiecode
}
Implementatie met Gin-framework
Laten we een volledig voorbeeld implementeren met Gin. Definieer eerst je datamodellen met struct-tags:
type Product struct {
ID int `json:"id" example:"1"`
Naam string `json:"naam" example:"Laptop" binding:"required"`
Beschrijving string `json:"beschrijving" example:"Hoogprestatie laptop"`
Prijs float64 `json:"prijs" example:"999.99" binding:"required,gt=0"`
Voorraad int `json:"voorraad" example:"50"`
}
type ErrorResponse struct {
Fout string `json:"fout" example:"Ongeldige invoer"`
Bericht string `json:"bericht" example:"Productnaam is vereist"`
}
Annoteer nu je handlerfuncties. Bij het werken met databasebewerkingen, helpen deze annotaties bij het documenteren van de gegevensstroom:
// GetProduct godoc
// @Summary Product ophalen op basis van ID
// @Description Haal een enkel product op op basis van zijn unieke identificator
// @Tags producten
// @Accept json
// @Produce json
// @Param id path int true "Product 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")
// Implementatie hier
c.JSON(200, Product{ID: 1, Naam: "Laptop", Prijs: 999.99})
}
// CreateProduct godoc
// @Summary Een nieuw product aanmaken
// @Description Voeg een nieuw product toe aan het catalogus
// @Tags producten
// @Accept json
// @Produce json
// @Param product body Product true "Productobject"
// @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{Fout: "Foutieve aanvraag", Bericht: err.Error()})
return
}
// Opslaan in database
c.JSON(201, product)
}
Documentatie genereren
Na het annoteren van je code, genereer de Swagger-documentatie:
swag init
Dit maakt een docs-map met swagger.json, swagger.yaml en Go-bestanden. Importeer en registreer het Swagger-eindpunt:
package main
import (
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
_ "yourproject/docs" // Importeer gegenereerde docs
)
func main() {
r := gin.Default()
// API routes
v1 := r.Group("/api/v1")
{
v1.GET("/products/:id", GetProduct)
v1.POST("/products", CreateProduct)
}
// Swagger-eindpunt
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
r.Run(":8080")
}
Nu kun je je interactieve API-documentatie bezoeken op http://localhost:8080/swagger/index.html.
Implementatie met Echo-framework
Echo-gebruikers volgen een vergelijkbaar patroon maar met Echo-specifieke middleware:
package main
import (
"github.com/labstack/echo/v4"
echoSwagger "github.com/swaggo/echo-swagger"
_ "yourproject/docs"
)
func main() {
e := echo.New()
// API routes
api := e.Group("/api/v1")
api.GET("/products/:id", getProduct)
api.POST("/products", createProduct)
// Swagger-eindpunt
e.GET("/swagger/*", echoSwagger.WrapHandler)
e.Start(":8080")
}
Implementatie met Fiber-framework
Fibers implementatie is net zo eenvoudig:
package main
import (
"github.com/gofiber/fiber/v2"
"github.com/gofiber/swagger"
_ "yourproject/docs"
)
func main() {
app := fiber.New()
// API routes
api := app.Group("/api/v1")
api.Get("/products/:id", getProduct)
api.Post("/products", createProduct)
// Swagger-eindpunt
app.Get("/swagger/*", swagger.HandlerDefault)
app.Listen(":8080")
}
Geavanceerde Swagger-annotaties
Documenteren van complexe aanvraaglichamen
Voor geneste structuren of 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"`
Aantal int `json:"aantal" example:"2" binding:"required,min=1"`
}
type Address struct {
Straat string `json:"straat" example:"123 Main St" binding:"required"`
Stad string `json:"stad" example:"New York" binding:"required"`
Postcode string `json:"postcode" example:"10001" binding:"required"`
}
// CreateOrder godoc
// @Summary Een nieuw bestelling aanmaken
// @Description Maak een bestelling met meerdere items en verzendinformatie
// @Tags bestellingen
// @Accept json
// @Produce json
// @Param order body CreateOrderRequest true "Bestelgegevens"
// @Success 201 {object} Bestelling
// @Failure 400 {object} ErrorResponse
// @Failure 422 {object} ErrorResponse
// @Security Bearer
// @Router /orders [post]
func CreateOrder(c *gin.Context) {
// Implementatie
}
Documenteren van bestandsuploads
// UploadImage godoc
// @Summary Productafbeelding uploaden
// @Description Upload een afbeeldingsbestand voor een product
// @Tags producten
// @Accept multipart/form-data
// @Produce json
// @Param id path int true "Product ID"
// @Param bestand formData file true "Afbeeldingsbestand"
// @Success 200 {object} map[string]string
// @Failure 400 {object} ErrorResponse
// @Security Bearer
// @Router /products/{id}/image [post]
func UploadImage(c *gin.Context) {
bestand, _ := c.FormFile("bestand")
// Uploadverwerking
}
Queryparameters en pagineren
// ListProducts godoc
// @Summary Productenlijst met pagineren
// @Description Ontvang een paginerende lijst van producten met optionele filteren
// @Tags producten
// @Accept json
// @Produce json
// @Param pagina query int false "Paginanummer" default(1)
// @Param pagina_grootte query int false "Items per pagina" default(10)
// @Param categorie query string false "Filter op categorie"
// @Param min_prijs query number false "Minimale prijs"
// @Param max_prijs query number false "Maximale prijs"
// @Success 200 {array} Product
// @Failure 400 {object} ErrorResponse
// @Router /products [get]
func ListProducts(c *gin.Context) {
// Implementatie met pagineren
}
Authenticatie en beveiliging
Documenteer verschillende authenticatiemethoden in je API. Voor multi-tenant toepassingen, is juiste authenticatie-documentatie cruciaal:
Bearer Token Authenticatie
// @securityDefinitions.apikey Bearer
// @in header
// @name Authorization
// @description Type "Bearer" gevolgd door een spatie en JWT-token.
API-sleutel authenticatie
// @securityDefinitions.apikey ApiKeyAuth
// @in header
// @name X-API-Key
// @description API-sleutel voor authenticatie
OAuth2 authenticatie
// @securitydefinitions.oauth2.application OAuth2Application
// @tokenUrl https://example.com/oauth/token
// @scope.write Schrijfrechten verlenen
// @scope.admin Schrijf- en leesrechten verlenen voor administratieve informatie
Basisauthenticatie
// @securityDefinitions.basic BasicAuth
Toepassen van beveiliging op specifieke eindpunten:
// @Security Bearer
// @Security ApiKeyAuth
Aanpassen van Swagger UI
Je kunt het uiterlijk en gedrag van de Swagger UI aanpassen:
// Aangepaste configuratie
url := ginSwagger.URL("http://localhost:8080/swagger/doc.json")
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler, url))
// Met aangepast titel
r.GET("/swagger/*any", ginSwagger.WrapHandler(
swaggerFiles.Handler,
ginSwagger.URL("http://localhost:8080/swagger/doc.json"),
ginSwagger.DefaultModelsExpandDepth(-1),
))
Swagger uitschakelen in productie:
if os.Getenv("ENV") != "productie" {
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
}
Integreren met CI/CD
Automatiseer de generatie van Swagger-documentatie in je CI/CD-pijplijn:
# GitHub Actions-voorbeeld
naam: Genereer Swagger Docs
op: [push]
taken:
swagger:
runs-on: ubuntu-latest
stappen:
- gebruikt: actions/checkout@v3
- naam: Stel Go in
gebruikt: actions/setup-go@v4
met:
go-versie: '1.21'
- naam: Installeer swag
uitvoeren: go install github.com/swaggo/swag/cmd/swag@latest
- naam: Genereer Swagger docs
uitvoeren: swag init
- naam: Commit docs
uitvoeren: |
git config user.name github-actions
git config user.email github-actions@github.com
git add docs/
git commit -m "Update Swagger-documentatie" || exit 0
git push
Beste praktijken
1. Consistente annotatiestijl
Houd een consistente opmaak over alle eindpunten:
// HandlerNaam godoc
// @Summary Korte beschrijving (onder 50 tekens)
// @Description Gedetailleerde beschrijving van wat het eindpunt doet
// @Tags bronnaam
// @Accept json
// @Produce json
// @Param naam locatie type vereist "beschrijving"
// @Success 200 {object} ResponseType
// @Failure 400 {object} ErrorResponse
// @Router /pad [methode]
2. Gebruik beschrijvende voorbeelden
Voeg realistische voorbeelden toe om API-gebruikers te helpen:
type User struct {
ID int `json:"id" example:"1"`
Email string `json:"email" example:"user@example.com"`
Aangemaakt time.Time `json:"aangemaakt" example:"2025-01-15T10:30:00Z"`
}
3. Documenteer alle responscodes
Voeg alle mogelijke HTTP-statuscodes toe:
// @Success 200 {object} Product
// @Success 201 {object} Product
// @Failure 400 {object} ErrorResponse "Foutieve aanvraag"
// @Failure 401 {object} ErrorResponse "Niet geautoriseerd"
// @Failure 403 {object} ErrorResponse "Verboden"
// @Failure 404 {object} ErrorResponse "Niet gevonden"
// @Failure 422 {object} ErrorResponse "Validatiefout"
// @Failure 500 {object} ErrorResponse "Interne serverfout"
4. Versieer je API
Gebruik juiste versieering in basispad:
// @BasePath /api/v1
En organiseer je code overeenkomstig:
v1 := r.Group("/api/v1")
v2 := r.Group("/api/v2")
5. Groepeer verwante eindpunten
Gebruik tags om eindpunten logisch te organiseren:
// @Tags producten
// @Tags bestellingen
// @Tags gebruikers
6. Houd de documentatie up-to-date
Voer swag init uit voor elke commit of integreer het in je bouwproces:
#!/bin/bash
# pre-commit hook
swag init
git add docs/
Testen van Swagger-documentatie
Bij het werken met serverloze architecturen zoals AWS Lambda, wordt het testen van je API-documentatie nog belangrijker:
func TestSwaggerGeneration(t *testing.T) {
// Controleer of swagger.json bestaat
_, err := os.Stat("./docs/swagger.json")
if err != nil {
t.Fatal("swagger.json niet gevonden, voer 'swag init' uit")
}
// Controleer of swagger-eindpunt reageert
r := setupRouter()
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/swagger/index.html", nil)
r.ServeHTTP(w, req)
assert.Equal(t, 200, w.Code)
}
Veelvoorkomende problemen en oplossingen
Documentatie die niet bijwerkt
Als wijzigingen niet verschijnen, zorg er dan voor dat je de documentatie opnieuw genereert:
swag init --parseDependency --parseInternal
De --parseDependency-vlag parseert externe afhankelijkheden, en --parseInternal parseert interne pakketten.
Aangepaste typen niet herkend
Voor typen van externe pakketten, gebruik de swaggertype-tag:
type CustomTime struct {
time.Time
}
func (CustomTime) SwaggerDoc() map[string]string {
return map[string]string{
"time": "RFC3339 tijdstip",
}
}
Of gebruik de swaggertype-tag:
type Product struct {
ID int `json:"id"`
Aangepast CustomTime `json:"aangepast" swaggertype:"string" format:"date-time"`
}
Arrays en enums
Documenteer arraytypen en enums:
type Filter struct {
Status []string `json:"status" enums:"actief,inactief,uitstaand"`
Tags []string `json:"tags"`
}
// @Param status query string false "Statusfilter" Enums(actief, inactief, uitstaand)
Alternatieve aanpakken
Hoewel swaggo de populairste keuze is, bestaan er ook andere opties:
go-swagger
Een meer functie-rijke maar complexere alternatief:
brew install go-swagger
swagger generate spec -o ./swagger.json
Handmatige OpenAPI-bestanden
Voor volledige controle, schrijf OpenAPI-specificaties handmatig in YAML:
openapi: 3.0.0
info:
title: Product API
version: 1.0.0
paths:
/products:
get:
summary: Productenlijst
responses:
'200':
description: Succes
Dan dienen met:
r.StaticFile("/openapi.yaml", "./openapi.yaml")
Integratie met AI en LLMs
Bij het bouwen van API’s die integreren met AI-diensten, wordt juiste documentatie cruciaal. Bijvoorbeeld, bij het werken met gestructureerde LLM-uitvoer, helpt Swagger bij het documenteren van complexe aanvraag- en reactieschema’s:
type LLMRequest struct {
Prompt string `json:"prompt" example:"Deze tekst samenvatten"`
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 Genereer gestructureerde LLM-uitvoer
// @Description Genereer tekst met beperkte uitvoerschema
// @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) {
// Implementatie
}
Prestatieoverwegingen
Swagger-documentatie heeft minimaal invloed op prestaties:
- Buildtijd:
swag initduurt 1-3 seconden voor de meeste projecten - Runtime: Documentatie wordt één keer bij het opstarten geladen
- Geheugen: Voegt meestal 1-2MB toe aan de binaire grootte
- Responsentijd: Geen invloed op API-eindpunten zelf
Voor zeer grote API’s (100+ eindpunten), overweeg:
- Het splitsen in meerdere Swagger-bestanden
- Lazy-loading van Swagger UI-assets
- Het serveren van documentatie vanaf een aparte service
Beveiligingsoverwegingen
Bij het blootstellen van Swagger-documentatie:
- Uitschakelen in productie (als de API intern is):
if os.Getenv("ENV") == "productie" {
// Register Swagger-eindpunt niet
return
}
- Authenticatie toevoegen:
geauthificeerd := r.Group("/swagger")
geauthificeerd.Use(AuthMiddleware())
geauthificeerd.GET("/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
- Beperk het eindpunt:
r.GET("/swagger/*any", RateLimitMiddleware(), ginSwagger.WrapHandler(swaggerFiles.Handler))
- Nooit interne details blootgeven:
- Documenteer geen interne eindpunten
- Vermijd het blootgeven van database-schema’s
- Sanitize foutmeldingen in documentatie
Conclusie
Het toevoegen van Swagger-documentatie aan je Go API verandert de ontwikkelaarservaring van gokken naar geleide verkennings. De swaggo-bibliotheek maakt dit proces eenvoudig door uitgebreide OpenAPI-documentatie te genereren vanuit je code-annotaties.
Belangrijke conclusies:
- Start met basisannotaties en breid geleidelijk uit
- Houd documentatie in synchronisatie met code via CI/CD
- Gebruik Swagger UI voor interactief testen tijdens ontwikkeling
- Documenteer authenticatie, fouten en randgevallen grondig
- Overweeg beveiligingsimplicaties bij het blootstellen van documentatie
Of je nu microservices, openbare API’s of interne tools bouwt, levert Swagger-documentatie dividend in het verminderen van ondersteuningslast, snellere opvang en betere API-design. De initiële investering in het leren van annotatiesyntax wordt snel routine, en automatische generatie zorgt ervoor dat je documentatie nooit achterblijft bij je implementatie.
Voor Go-ontwikkelaars creëert de combinatie van sterke typing, codegeneratie en de swaggo-annotatiesysteem een krachtige workflow die API-documentatie een natuurlijk onderdeel van het ontwikkelproces maakt in plaats van een na-actie.
Nuttige links
- Go Cheat sheet
- REST API’s bouwen in Go
- Go ORMs voor PostgreSQL-vergelijking: GORM vs Ent vs Bun vs sqlc
- Multi-tenant databasepatronen met voorbeelden in Go
- LLMs met gestructureerde uitvoer: Ollama, Qwen3 & Python of Go
- AWS Lambda-prestaties: JavaScript vs Python vs Go