Adicionando Swagger à sua API Go

Gerar automaticamente documentação OpenAPI a partir de anotações no código

Conteúdo da página

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.

swagger api specs on agile board 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 init leva 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:

  1. Desative em Produção (se a API for interna):
if os.Getenv("ENV") == "production" {
    // Não registre o endpoint do Swagger
    return
}
  1. Adicione Autenticação:
authorized := r.Group("/swagger")
authorized.Use(AuthMiddleware())
authorized.GET("/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
  1. Limite a Taxa do Endpoint:
r.GET("/swagger/*any", RateLimitMiddleware(), ginSwagger.WrapHandler(swaggerFiles.Handler))
  1. 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.

Recursos Externos