Adicionando Swagger à sua API Go
Gerar automaticamente documentação OpenAPI a partir de anotações no código
A documentação da API é crucial para qualquer aplicação moderna, e para Go APIs Swagger (OpenAPI) tornou-se o padrão da indústria. Para desenvolvedores Go, o swaggo oferece uma solução elegante para gerar documentação abrangente diretamente das anotações do código.
Esta imagem agradável é gerada pelo modelo AI Flux 1 dev.
Por que o Swagger importa para APIs Go
Ao construir APIs REST, a documentação frequentemente se torna obsoleta à medida que o código evolui. O Swagger resolve isso gerando documentação a partir do seu código-fonte, garantindo que ela permaneça sincronizada com sua implementação. A interface interativa do Swagger UI permite que os desenvolvedores testem endpoints diretamente a partir do navegador, melhorando significativamente a experiência do desenvolvedor.
Para equipes que estão construindo microserviços ou APIs públicas, a documentação do Swagger torna-se essencial para:
- Geração de Cliente: Crie automaticamente bibliotecas de cliente em múltiplos idiomas
- Teste de Contrato: Valide solicitações e respostas contra esquemas definidos
- Colaboração em Equipe: Forneça 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 ao Swagger em aplicações 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 CLI swag:
go install github.com/swaggo/swag/cmd/swag@latest
Em seguida, adicione o pacote de middleware apropriado do Swagger para seu framework. Para Gin:
go get -u github.com/swaggo/gin-swagger
go get -u github.com/swaggo/files
Para Echo:
go get -u github.com/swaggo/echo-swagger
Para Fiber:
go get -u github.com/gofiber/swagger
Configuração Básica
Comece adicionando informações gerais da API no seu arquivo main.go. Similar a como você estruturaria uma API REST em Go, as anotações devem ser claras e descritivas:
// @title Product API
// @version 1.0
// @description Uma API de gerenciamento de produtos com documentação do Swagger
// @termsOfService http://swagger.io/terms/
// @contact.name Suporte da API
// @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" seguido de um espaço e um token JWT.
func main() {
// Seu código da aplicação
}
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 Obter produto por ID
// @Description Recuperar um único produto por seu identificador único
// @Tags produtos
// @Accept json
// @Produce json
// @Param id path int true "ID do produto"
// @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")
// Implementação aqui
c.JSON(200, Product{ID: 1, Name: "Laptop", Price: 999.99})
}
// CreateProduct godoc
// @Summary Criar um novo produto
// @Description Adicionar um novo produto ao catálogo
// @Tags produtos
// @Accept json
// @Produce json
// @Param product body Product true "Objeto de produto"
// @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: "Requisição inválida", Message: err.Error()})
return
}
// Salvar no banco de dados
c.JSON(201, product)
}
Gerando Documentação
Depois de anotar seu código, gere a documentação do Swagger:
swag init
Isso cria uma pasta docs com swagger.json, swagger.yaml e arquivos Go. Importe e registre o endpoint do Swagger:
package main
import (
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
_ "yourproject/docs" // Importe as documentações geradas
)
func main() {
r := gin.Default()
// Rotas da API
v1 := r.Group("/api/v1")
{
v1.GET("/products/:id", GetProduct)
v1.POST("/products", CreateProduct)
}
// Endpoint do Swagger
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
r.Run(":8080")
}
Agora, acesse sua documentação interativa da API em http://localhost:8080/swagger/index.html.
Implementação com o Framework Echo
Os usuários do Echo seguem um padrão semelhante, 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()
// Rotas da API
api := e.Group("/api/v1")
api.GET("/products/:id", getProduct)
api.POST("/products", createProduct)
// Endpoint do Swagger
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()
// Rotas da API
api := app.Group("/api/v1")
api.Get("/products/:id", getProduct)
api.Post("/products", createProduct)
// Endpoint do Swagger
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 Criar um novo pedido
// @Description Criar um pedido com vários itens e informações de entrega
// @Tags pedidos
// @Accept json
// @Produce json
// @Param order body CreateOrderRequest true "Detalhes do pedido"
// @Success 201 {object} Order
// @Failure 400 {object} ErrorResponse
// @Failure 422 {object} ErrorResponse
// @Security Bearer
// @Router /orders [post]
func CreateOrder(c *gin.Context) {
// Implementação
}
Documentando Uploads de Arquivo
// UploadImage godoc
// @Summary Carregar imagem de produto
// @Description Carregar um arquivo de imagem para um produto
// @Tags produtos
// @Accept multipart/form-data
// @Produce json
// @Param id path int true "ID do produto"
// @Param file formData file true "Arquivo de imagem"
// @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")
// Lidar com o upload
}
Parâmetros de Consulta e Paginação
// ListProducts godoc
// @Summary Listar produtos com paginação
// @Description Obter lista paginada de produtos com filtragem opcional
// @Tags produtos
// @Accept json
// @Produce json
// @Param page query int false "Número da página" default(1)
// @Param page_size query int false "Itens por página" default(10)
// @Param category query string false "Filtrar por categoria"
// @Param min_price query number false "Preço mínimo"
// @Param max_price query number false "Preço máximo"
// @Success 200 {array} Product
// @Failure 400 {object} ErrorResponse
// @Router /products [get]
func ListProducts(c *gin.Context) {
// Implementação com paginação
}
Autenticação e Segurança
Documente diferentes métodos de autenticação em sua API. Para aplicações multi-tenant, a documentação adequada de autenticação é crucial:
Autenticação com Token Bearer
// @securityDefinitions.apikey Bearer
// @in header
// @name Authorization
// @description Type "Bearer" seguido de um espaço e um token JWT.
Autenticação com Chave API
// @securityDefinitions.apikey ApiKeyAuth
// @in header
// @name X-API-Key
// @description Chave API para autenticação
Autenticação OAuth2
// @securitydefinitions.oauth2.application OAuth2Application
// @tokenUrl https://example.com/oauth/token
// @scope.write Permite acesso de escrita
// @scope.admin Permite acesso de leitura e escrita a informações administrativas
Autenticação Básica
// @securityDefinitions.basic BasicAuth
Aplique segurança a endpoints específicos:
// @Security Bearer
// @Security ApiKeyAuth
Personalizando a Interface do Swagger UI
Você pode personalizar a aparência e o comportamento da interface do Swagger UI:
// Configuração personalizada
url := ginSwagger.URL("http://localhost:8080/swagger/doc.json")
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler, url))
// Com título personalizado
r.GET("/swagger/*any", ginSwagger.WrapHandler(
swaggerFiles.Handler,
ginSwagger.URL("http://localhost:8080/swagger/doc.json"),
ginSwagger.DefaultModelsExpandDepth(-1),
))
Para desativar 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 da documentação do Swagger em sua pipeline CI/CD:
# Exemplo de GitHub Actions
name: Gerar Documentação do Swagger
on: [push]
jobs:
swagger:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Configurar Go
uses: actions/setup-go@v4
with:
go-version: '1.21'
- name: Instalar swag
run: go install github.com/swaggo/swag/cmd/swag@latest
- name: Gerar documentação do Swagger
run: swag init
- name: Commitar documentos
run: |
git config user.name github-actions
git config user.email github-actions@github.com
git add docs/
git commit -m "Atualizar documentação do Swagger" || exit 0
git push
Boas Práticas
1. Estilo Consistente de Anotação
Mantenha um estilo de formatação consistente em todos os endpoints:
// HandlerName godoc
// @Summary Breve descrição (menos de 50 caracteres)
// @Description Descrição detalhada do que o endpoint faz
// @Tags nome-do-recurso
// @Accept json
// @Produce json
// @Param nome localização tipo necessário "descrição"
// @Success 200 {object} TipoDeResposta
// @Failure 400 {object} ErrorResponse
// @Router /caminho [método]
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 possíveis códigos de status HTTP:
// @Success 200 {object} Product
// @Success 201 {object} Product
// @Failure 400 {object} ErrorResponse "Requisição Inválida"
// @Failure 401 {object} ErrorResponse "Não Autorizado"
// @Failure 403 {object} ErrorResponse "Proibido"
// @Failure 404 {object} ErrorResponse "Não Encontrado"
// @Failure 422 {object} ErrorResponse "Erro de Validação"
// @Failure 500 {object} ErrorResponse "Erro Interno do Servidor"
4. Versione sua API
Use a versão adequada no caminho base:
// @BasePath /api/v1
E organize seu código conforme necessário:
v1 := r.Group("/api/v1")
v2 := r.Group("/api/v2")
5. Agrupe Endpoints Relacionados
Use tags para organizar endpoints logicamente:
// @Tags produtos
// @Tags pedidos
// @Tags usuários
6. Mantenha a Documentação Atualizada
Execute swag init antes de cada commit ou integre-o ao seu processo de construção:
#!/bin/bash
# hook de pre-commit
swag init
git add docs/
Testando a Documentação do Swagger
Ao trabalhar com arquiteturas serverless como AWS Lambda, testar sua documentação da API torna-se ainda mais importante:
func TestSwaggerGeneration(t *testing.T) {
// Verificar se swagger.json existe
_, err := os.Stat("./docs/swagger.json")
if err != nil {
t.Fatal("swagger.json não encontrado, execute 'swag init'")
}
// Verificar se o endpoint swagger responde
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 estar regenerando as documentações:
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": "Timestamp RFC3339",
}
}
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:"ativo,inativo,pendente"`
Tags []string `json:"tags"`
}
// @Param status query string false "Filtro de status" Enums(ativo, inativo, pendente)
Abordagens Alternativas
Embora o swaggo seja a escolha mais popular, existem outras opções:
go-swagger
Uma alternativa mais rica em recursos, mas mais 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: API de Produtos
version: 1.0.0
paths:
/products:
get:
summary: Listar produtos
responses:
'200':
description: Sucesso
E então sirva com:
r.StaticFile("/openapi.yaml", "./openapi.yaml")
Integrando com IA e LLMs
Ao construir APIs que se integram com serviços de IA, a documentação adequada torna-se crucial. Por exemplo, ao trabalhar com saídas estruturadas de LLM, o Swagger ajuda a documentar esquemas complexos de solicitações e respostas:
type LLMRequest struct {
Prompt string `json:"prompt" example:"Resuma este texto"`
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 Gerar saída estruturada de LLM
// @Description Gerar texto com esquema de saída restrito
// @Tags llm
// @Accept json
// @Produce json
// @Param request body LLMRequest true "Parâmetros de LLM"
// @Success 200 {object} map[string]interface{}
// @Failure 400 {object} ErrorResponse
// @Router /llm/generate [post]
func GenerateStructured(c *gin.Context) {
// Implementação
}
Considerações de Desempenho
A documentação do Swagger tem impacto mínimo no desempenho:
- Tempo de Construção:
swag initleva 1-3 segundos para a maioria dos projetos - Tempo de Execução: A documentação é carregada uma vez no início
- Memória: Normalmente adiciona 1-2MB ao tamanho do binário
- Tempo de Resposta: Não impacta os próprios endpoints da API
Para APIs muito grandes (mais de 100 endpoints), considere:
- Dividir em múltiplos arquivos de Swagger
- Carregar a interface do Swagger UI de forma lazy
- Servir a documentação de um serviço separado
Considerações de Segurança
Ao expor a documentação do Swagger:
- Desative em Produção (se a API for interna):
if os.Getenv("ENV") == "production" {
// Não registre o endpoint do Swagger
return
}
- Adicione Autenticação:
authorized := r.Group("/swagger")
authorized.Use(AuthMiddleware())
authorized.GET("/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
- Limite a Taxa do Endpoint:
r.GET("/swagger/*any", RateLimitMiddleware(), ginSwagger.WrapHandler(swaggerFiles.Handler))
- Nunca Exponha Detalhes Internos:
- Não documente endpoints internos
- Evite expor diretamente esquemas de banco de dados
- Sanitize mensagens de erro na documentação
Conclusão
Adicionar documentação do Swagger à sua API Go transforma a experiência do desenvolvedor de adivinhação para exploração guiada. A biblioteca swaggo torna esse processo direto gerando documentação OpenAPI abrangente a partir das anotações do seu código.
Principais lições aprendidas:
- Comece com anotações básicas e expanda gradualmente
- Mantenha a documentação sincronizada com o código por meio de CI/CD
- Use a interface do Swagger UI para testes interativos durante o desenvolvimento
- Documente autenticação, erros e casos de borda de forma abrangente
- Considere as implicações de segurança ao expor a documentação
Seja você construindo microserviços, APIs públicas ou ferramentas internas, a documentação do Swagger paga dividendos em redução da carga de suporte, onboarding mais rápido e melhor design de APIs. O investimento inicial no aprendizado da sintaxe de anotação rapidamente se torna rotina, e a geração automatizada garante que sua documentação nunca fique para trás da sua implementação.
Para desenvolvedores Go, a combinação de tipagem forte, geração de código e sistema de anotações do swaggo cria um fluxo de trabalho poderoso que torna a documentação da API uma parte natural do processo de desenvolvimento, em vez de um pós-graduação.
Links Úteis
- Go Cheatsheet
- Construindo APIs REST em Go
- Comparando ORMs Go para PostgreSQL: GORM vs Ent vs Bun vs sqlc
- Padrões de Banco de Dados Multi-Tenant com exemplos em Go
- LLMs com Saída Estruturada: Ollama, Qwen3 e Python ou Go
- Desempenho de AWS Lambda: JavaScript vs Python vs Golang