Adicionando Swagger à sua API Go

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

Conteúdo da página

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.

especificações de API swagger no quadro ágil 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 init leva 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:

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

Recursos Externos