Adicionando Swagger à sua API Go
Gerar automaticamente a documentação OpenAPI a partir de anotações de código
A documentação de API é crucial para qualquer aplicação moderna, e para APIs Go Swagger (OpenAPI) tornou-se o padrão da indústria. Para desenvolvedores Go, o swaggo oferece uma solução elegante para gerar documentação completa de API diretamente a partir de anotações de código.
Esta bela imagem foi gerada pelo modelo de IA Flux 1 dev.
Por que o Swagger é Importante para APIs Go
Ao construir APIs REST, a documentação frequentemente fica desatualizada à medida que o código evolui. O Swagger resolve isso gerando documentação diretamente do seu código-fonte, garantindo que ela permaneça sincronizada com a sua implementação. A interface Swagger interativa permite que os desenvolvedores testem endpoints diretamente do navegador, melhorando significativamente a experiência do desenvolvedor.
Para equipes que constroem microsserviços ou APIs públicas, a documentação Swagger torna-se essencial para:
- Geração de Clientes: Criar automaticamente bibliotecas de cliente em várias linguagens
- Testes de Contrato: Validar solicitações e respostas contra esquemas definidos
- Colaboração da Equipe: Fornecer uma única fonte de verdade para contratos de API
- Onboarding de Desenvolvedores: Novos membros da equipe podem explorar APIs de forma interativa
Começando com o swaggo
A biblioteca swaggo é a ferramenta mais popular para adicionar suporte Swagger a aplicativos Go. Ela funciona analisando comentários especiais no seu código e gerando arquivos de especificação OpenAPI 3.0.
Instalação
Primeiro, instale a ferramenta de linha de comando swag:
go install github.com/swaggo/swag/cmd/swag@latest
Em seguida, adicione o pacote de middleware Swagger apropriado para o seu framework. Para o Gin:
go get -u github.com/swaggo/gin-swagger
go get -u github.com/swaggo/files
Para o Echo:
go get -u github.com/swaggo/echo-swagger
Para o Fiber:
go get -u github.com/gofiber/swagger
Configuração Básica
Comece adicionando informações gerais da API no seu arquivo main.go. Assim como você estruturaria uma API REST em Go, as anotações devem ser claras e descritivas:
// @title Product API
// @version 1.0
// @description A product management API with Swagger documentation
// @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 Type "Bearer" followed by a space and JWT token.
func main() {
// Your application code
}
Implementação com o Framework Gin
Vamos implementar um exemplo completo usando o Gin. Primeiro, defina seus modelos de dados com tags de struct:
type Product struct {
ID int `json:"id" example:"1"`
Name string `json:"name" example:"Laptop" binding:"required"`
Description string `json:"description" example:"High-performance 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:"Invalid input"`
Message string `json:"message" example:"Product name is required"`
}
Agora, anote suas funções de manipulador. Ao trabalhar com operações de banco de dados, essas anotações ajudam a documentar o fluxo de dados:
// GetProduct godoc
// @Summary Get product by ID
// @Description Retrieve a single product by its unique identifier
// @Tags products
// @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")
// Implementation here
c.JSON(200, Product{ID: 1, Name: "Laptop", Price: 999.99})
}
// CreateProduct godoc
// @Summary Create a new product
// @Description Add a new product to the catalog
// @Tags products
// @Accept json
// @Produce json
// @Param product body Product true "Product object"
// @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
}
// Save to database
c.JSON(201, product)
}
Gerando Documentação
Depois de anotar seu código, gere a documentação Swagger:
swag init
Isso cria uma pasta docs com swagger.json, swagger.yaml e arquivos Go. Importe e registre o endpoint Swagger:
package main
import (
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
_ "yourproject/docs" // Import generated docs
)
func main() {
r := gin.Default()
// API routes
v1 := r.Group("/api/v1")
{
v1.GET("/products/:id", GetProduct)
v1.POST("/products", CreateProduct)
}
// Swagger endpoint
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
r.Run(":8080")
}
Agora acesse sua documentação de API interativa em http://localhost:8080/swagger/index.html.
Implementação com o Framework Echo
Usuários do Echo seguem um padrão similar, mas com middleware específico do Echo:
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 endpoint
e.GET("/swagger/*", echoSwagger.WrapHandler)
e.Start(":8080")
}
Implementação com o Framework Fiber
A implementação do Fiber é igualmente direta:
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 endpoint
app.Get("/swagger/*", swagger.HandlerDefault)
app.Listen(":8080")
}
Anotações Avançadas do Swagger
Documentando Corpos de Solicitação Complexos
Para estruturas aninhadas ou 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 Create a new order
// @Description Create an order with multiple items and shipping information
// @Tags orders
// @Accept json
// @Produce json
// @Param order body CreateOrderRequest true "Order details"
// @Success 201 {object} Order
// @Failure 400 {object} ErrorResponse
// @Failure 422 {object} ErrorResponse
// @Security Bearer
// @Router /orders [post]
func CreateOrder(c *gin.Context) {
// Implementation
}
Documentando Uploads de Arquivos
// UploadImage godoc
// @Summary Upload product image
// @Description Upload an image file for a product
// @Tags products
// @Accept multipart/form-data
// @Produce json
// @Param id path int true "Product ID"
// @Param file formData file true "Image file"
// @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 upload
}
Parâmetros de Consulta e Paginação
// ListProducts godoc
// @Summary List products with pagination
// @Description Get paginated list of products with optional filtering
// @Tags products
// @Accept json
// @Produce json
// @Param page query int false "Page number" default(1)
// @Param page_size query int false "Items per page" default(10)
// @Param category query string false "Filter by category"
// @Param min_price query number false "Minimum price"
// @Param max_price query number false "Maximum price"
// @Success 200 {array} Product
// @Failure 400 {object} ErrorResponse
// @Router /products [get]
func ListProducts(c *gin.Context) {
// Implementation with pagination
}
Autenticação e Segurança
Documente diferentes métodos de autenticação na sua API. Para aplicações multitenant, a documentação de autenticação adequada é crucial:
Autenticação por Token Bearer
// @securityDefinitions.apikey Bearer
// @in header
// @name Authorization
// @description Type "Bearer" followed by a space and JWT token.
Autenticação por Chave de API
// @securityDefinitions.apikey ApiKeyAuth
// @in header
// @name X-API-Key
// @description API key for authentication
Autenticação OAuth2
// @securitydefinitions.oauth2.application OAuth2Application
// @tokenUrl https://example.com/oauth/token
// @scope.write Grants write access
// @scope.admin Grants read and write access to administrative information
Autenticação Básica
// @securityDefinitions.basic BasicAuth
Aplique segurança a endpoints específicos:
// @Security Bearer
// @Security ApiKeyAuth
Personalizando a Interface Swagger
Você pode personalizar a aparência e o comportamento da interface Swagger:
// Custom configuration
url := ginSwagger.URL("http://localhost:8080/swagger/doc.json")
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler, url))
// With custom title
r.GET("/swagger/*any", ginSwagger.WrapHandler(
swaggerFiles.Handler,
ginSwagger.URL("http://localhost:8080/swagger/doc.json"),
ginSwagger.DefaultModelsExpandDepth(-1),
))
Para desabilitar o Swagger em produção:
if os.Getenv("ENV") != "production" {
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
}
Integração com CI/CD
Automatize a geração de documentação Swagger em seu pipeline de CI/CD:
# GitHub Actions example
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
Melhores Práticas
1. Estilo de Anotação Consistente
Mantenha a formatação consistente em todos os endpoints:
// HandlerName godoc
// @Summary Brief description (under 50 chars)
// @Description Detailed description of what the endpoint does
// @Tags resource-name
// @Accept json
// @Produce json
// @Param name location type required "description"
// @Success 200 {object} ResponseType
// @Failure 400 {object} ErrorResponse
// @Router /path [method]
2. Use Exemplos Descritivos
Adicione exemplos realistas para ajudar os consumidores da 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. Documente Todos os Códigos de Resposta
Inclua todos os códigos de status HTTP possíveis:
// @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. Versione Sua API
Use versionamento adequado no caminho base:
// @BasePath /api/v1
E organize seu código de acordo:
v1 := r.Group("/api/v1")
v2 := r.Group("/api/v2")
5. Agrupe Endpoints Relacionados
Use tags para organizar endpoints logicamente:
// @Tags products
// @Tags orders
// @Tags users
6. Mantenha a Documentação Atualizada
Execute swag init antes de cada commit ou integre ao seu processo de build:
#!/bin/bash
# pre-commit hook
swag init
git add docs/
Testando Documentação Swagger
Ao trabalhar com arquiteturas serverless como AWS Lambda, testar sua documentação de API torna-se ainda mais importante:
func TestSwaggerGeneration(t *testing.T) {
// Verify swagger.json exists
_, err := os.Stat("./docs/swagger.json")
if err != nil {
t.Fatal("swagger.json not found, run 'swag init'")
}
// Verify swagger endpoint responds
r := setupRouter()
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/swagger/index.html", nil)
r.ServeHTTP(w, req)
assert.Equal(t, 200, w.Code)
}
Problemas Comuns e Soluções
Documentação Não Atualizando
Se as alterações não aparecerem, certifique-se de que está regenerando a documentação:
swag init --parseDependency --parseInternal
A flag --parseDependency analisa dependências externas, e --parseInternal analisa pacotes internos.
Tipos Personalizados Não Reconhecidos
Para tipos de pacotes externos, use a tag swaggertype:
type CustomTime struct {
time.Time
}
func (CustomTime) SwaggerDoc() map[string]string {
return map[string]string{
"time": "RFC3339 timestamp",
}
}
Ou use a tag swaggertype:
type Product struct {
ID int `json:"id"`
UpdatedAt CustomTime `json:"updated_at" swaggertype:"string" format:"date-time"`
}
Arrays e Enums
Documente tipos de array e enums:
type Filter struct {
Status []string `json:"status" enums:"active,inactive,pending"`
Tags []string `json:"tags"`
}
// @Param status query string false "Status filter" Enums(active, inactive, pending)
Abordagens Alternativas
Embora o swaggo seja a escolha mais popular, outras opções existem:
go-swagger
Uma alternativa mais rica em recursos, mas complexa:
brew install go-swagger
swagger generate spec -o ./swagger.json
Arquivos OpenAPI Manuais
Para controle total, escreva especificações OpenAPI manualmente em YAML:
openapi: 3.0.0
info:
title: Product API
version: 1.0.0
paths:
/products:
get:
summary: List products
responses:
'200':
description: Success
E sirva com:
r.StaticFile("/openapi.yaml", "./openapi.yaml")
Integração com IA e LLMs
Ao construir APIs que integram serviços de IA, a documentação adequada torna-se crucial. Por exemplo, ao trabalhar com saídas estruturadas de LLMs, o Swagger ajuda a documentar esquemas de solicitação e resposta complexos:
type LLMRequest struct {
Prompt string `json:"prompt" example:"Summarize this text"`
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
}
Considerações de Desempenho
A documentação Swagger tem impacto mínimo no desempenho:
- Tempo de Build:
swag initleva 1-3 segundos para a maioria dos projetos - Tempo de Execução: A documentação é carregada uma vez na inicialização
- Memória: Tipicamente adiciona 1-2MB ao tamanho do binário
- Tempo de Resposta: Sem impacto nos próprios endpoints da API
Para APIs muito grandes (100+ endpoints), considere:
- Dividir em múltiplos arquivos Swagger
- Carregamento preguiçoso de ativos da interface Swagger
- Servir documentação de um serviço separado
Considerações de Segurança
Ao expor documentação Swagger:
- Desabilitar em Produção (se a API for interna):
if os.Getenv("ENV") == "production" {
// Don't register Swagger endpoint
return
}
- Adicionar Autenticação:
authorized := r.Group("/swagger")
authorized.Use(AuthMiddleware())
authorized.GET("/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
- Limitar Taxa do Endpoint:
r.GET("/swagger/*any", RateLimitMiddleware(), ginSwagger.WrapHandler(swaggerFiles.Handler))
- Nunca Expor Detalhes Internos:
- Não documente endpoints internos
- Evite expor esquemas de banco de dados diretamente
- Sanitize mensagens de erro na documentação
Conclusão
Adicionar documentação Swagger à sua API Go transforma a experiência do desenvolvedor de adivinhação para exploração guiada. A biblioteca swaggo torna este processo direto ao gerar documentação OpenAPI abrangente a partir de suas anotações de código.
Principais pontos:
- Comece com anotações básicas e expanda gradualmente
- Mantenha a documentação sincronizada com o código através de CI/CD
- Use a interface Swagger para testes interativos durante o desenvolvimento
- Documente autenticação, erros e casos extremos minuciosamente
- Considere implicações de segurança ao expor documentação
Seja construindo microsserviços, APIs públicas ou ferramentas internas, a documentação Swagger paga dividendos em redução de carga de suporte, onboarding mais rápido e melhor design de API. O investimento inicial em aprender a sintaxe de anotação rapidamente se torna rotineiro, e a geração automatizada garante que sua documentação nunca fique atrasada em relação à sua implementação.
Para desenvolvedores Go, a combinação de tipagem forte, geração de código e o sistema de anotação do swaggo cria um fluxo de trabalho poderoso que torna a documentação de API uma parte natural do processo de desenvolvimento, em vez de uma reflexão tardia.
Links Úteis
- Go Cheatsheet
- Building REST APIs in Go
- Comparing Go ORMs for PostgreSQL: GORM vs Ent vs Bun vs sqlc
- Multi-Tenancy Database Patterns with examples in Go
- LLMs with Structured Output: Ollama, Qwen3 & Python or Go
- AWS lambda performance: JavaScript vs Python vs Golang