Entendendo APIs e a Importância da Documentação
Uma API (Application Programming Interface) é o contrato entre seu servidor e seus clientes. Quando você cria uma API em Go, está definindo endpoints, métodos HTTP, formatos de requisição e resposta. A documentação de uma API não é um luxo — é uma necessidade fundamental. Sem ela, desenvolvedores que consumem sua API gastarão horas em tentativa e erro, abrindo issues desnecessários e tendo uma experiência frustrante.
A documentação tradicional (em arquivos Markdown, PDFs ou wikis) rapidamente fica desatualizada. Você muda um endpoint, adiciona um novo campo de validação, e a documentação permanece obsoleta. É aí que o Swagger entra em cena. O Swagger (agora chamado OpenAPI) é um padrão da indústria que permite documentar APIs de forma estruturada e automatizada. Melhor ainda: com o swaggo, você gera essa documentação diretamente a partir do seu código Go, usando anotações simples.
O que é Swagger/OpenAPI e por que usar swaggo
Swagger é uma especificação aberta que descreve APIs RESTful em formato YAML ou JSON. A especificação define todos os detalhes: quais endpoints existem, que métodos HTTP aceitam, quais são os parâmetros, como são estruturados os dados de entrada e saída, códigos de status HTTP, autenticação, e muito mais. Essa especificação pode ser lida por ferramentas que geram interfaces visuais interativas, permitindo que qualquer um explore e teste sua API sem escrever código.
O swaggo é uma ferramenta Go que lê anotações (comments especiais) no seu código-fonte e gera automaticamente a especificação Swagger/OpenAPI completa. Isso significa que sua documentação vive junto com seu código. Quando você muda uma função, atualiza a anotação ali mesmo, e a documentação se regenera. Não há sincronização manual, não há risco de divergência entre código e documentação.
Instalação e Configuração Inicial
Comece instalando o swaggo via go install. Você também precisará de um roteador HTTP em Go — neste artigo usaremos Gin, que é popular e simples, mas os conceitos aplicam-se a qualquer roteador.
go install github.com/swaggo/swag/cmd/swag@latest
Em seguida, inicialize um módulo Go e adicione as dependências necessárias:
go mod init github.com/seu-usuario/sua-api
go get -u github.com/gin-gonic/gin
go get -u github.com/swaggo/gin-swagger
go get -u github.com/swaggo/files
Crie a estrutura básica do seu projeto:
seu-projeto/
├── main.go
├── handlers/
│ └── users.go
├── models/
│ └── user.go
└── go.mod
Estruturando sua API com anotações Swagger
A magia do swaggo está nas anotações. Cada endpoint, cada modelo de dados, é documentado através de comentários especiais que seguem a sintaxe do Swagger. O swaggo lê essas anotações e gera a especificação OpenAPI automaticamente.
Anotações Globais da API
Comece definindo informações globais sobre sua API no arquivo main.go:
package main
import (
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
_ "github.com/seu-usuario/sua-api/docs"
)
// @title API de Gerenciamento de Usuários
// @version 1.0
// @description Uma API simples para demonstrar Swagger com swaggo
// @termsOfService http://swagger.io/terms/
// @contact.name API Support
// @contact.url http://www.swagger.io/support
// @license.name Apache 2.0
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html
// @host localhost:8080
// @BasePath /api/v1
// @schemes http https
// @securityDefinitions.apikey Bearer
// @in header
// @name Authorization
func main() {
router := gin.Default()
// Rota para acessar a documentação Swagger
router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
// Suas rotas de API aqui
api := router.Group("/api/v1")
{
api.POST("/users", CreateUser)
api.GET("/users/:id", GetUser)
api.PUT("/users/:id", UpdateUser)
api.DELETE("/users/:id", DeleteUser)
}
router.Run(":8080")
}
As anotações no topo do main.go definem informações globais: título da API, versão, descrição, informações de contato, segurança (no caso, um Bearer token). O import _ "github.com/seu-usuario/sua-api/docs" é importante — ele carrega o pacote docs gerado pelo swaggo.
Modelos de Dados com Tags Swagger
Defina seus modelos de dados em models/user.go:
package models
// User representa um usuário no sistema
type User struct {
// ID único do usuário
// example: 1
ID int `json:"id" example:"1"`
// Nome completo do usuário
// example: João Silva
Name string `json:"name" example:"João Silva"`
// Email do usuário (deve ser único)
// example: joao@example.com
Email string `json:"email" example:"joao@example.com"`
// Idade do usuário
// example: 30
Age int `json:"age" example:"30"`
}
// CreateUserRequest representa os dados necessários para criar um usuário
type CreateUserRequest struct {
// Nome do usuário (obrigatório)
// minlength: 3
// maxlength: 100
Name string `json:"name" binding:"required,min=3,max=100" example:"Maria Santos"`
// Email do usuário (obrigatório)
Email string `json:"email" binding:"required,email" example:"maria@example.com"`
// Idade do usuário (obrigatório)
// minimum: 18
// maximum: 120
Age int `json:"age" binding:"required,min=18,max=120" example:"25"`
}
Repare que não há tags especiais swagger: aqui. O swaggo é inteligente o suficiente para ler os tags JSON e os comentários acima dos campos.
Endpoints com Documentação Detalhada
Agora documente seus handlers em handlers/users.go:
package handlers
import (
"net/http"
"strconv"
"github.com/gin-gonic/gin"
"github.com/seu-usuario/sua-api/models"
)
// CreateUser cria um novo usuário
// @Summary Criar um novo usuário
// @Description Cria um novo usuário no sistema com os dados fornecidos
// @Tags users
// @Accept json
// @Produce json
// @Param request body models.CreateUserRequest true "Dados do usuário a ser criado"
// @Success 201 {object} models.User "Usuário criado com sucesso"
// @Failure 400 {object} map[string]string "Erro de validação"
// @Failure 500 {object} map[string]string "Erro interno do servidor"
// @Router /users [post]
// @Security Bearer
func CreateUser(c *gin.Context) {
var req models.CreateUserRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
user := models.User{
ID: 1, // Em produção, isso seria um ID gerado
Name: req.Name,
Email: req.Email,
Age: req.Age,
}
c.JSON(http.StatusCreated, user)
}
// GetUser obtém um usuário pelo ID
// @Summary Obter usuário por ID
// @Description Retorna os dados completos de um usuário específico
// @Tags users
// @Accept json
// @Produce json
// @Param id path int true "ID do usuário"
// @Success 200 {object} models.User "Usuário encontrado"
// @Failure 404 {object} map[string]string "Usuário não encontrado"
// @Router /users/{id} [get]
// @Security Bearer
func GetUser(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "ID inválido"})
return
}
// Simulação: retornar usuário
user := models.User{
ID: id,
Name: "João Silva",
Email: "joao@example.com",
Age: 30,
}
c.JSON(http.StatusOK, user)
}
// UpdateUser atualiza um usuário existente
// @Summary Atualizar usuário
// @Description Atualiza os dados de um usuário existente
// @Tags users
// @Accept json
// @Produce json
// @Param id path int true "ID do usuário"
// @Param request body models.CreateUserRequest true "Dados a serem atualizados"
// @Success 200 {object} models.User "Usuário atualizado"
// @Failure 400 {object} map[string]string "Erro de validação"
// @Failure 404 {object} map[string]string "Usuário não encontrado"
// @Router /users/{id} [put]
// @Security Bearer
func UpdateUser(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "ID inválido"})
return
}
var req models.CreateUserRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
user := models.User{
ID: id,
Name: req.Name,
Email: req.Email,
Age: req.Age,
}
c.JSON(http.StatusOK, user)
}
// DeleteUser deleta um usuário
// @Summary Deletar usuário
// @Description Remove um usuário do sistema permanentemente
// @Tags users
// @Accept json
// @Produce json
// @Param id path int true "ID do usuário"
// @Success 204 ""
// @Failure 404 {object} map[string]string "Usuário não encontrado"
// @Router /users/{id} [delete]
// @Security Bearer
func DeleteUser(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "ID inválido"})
return
}
// Simulação: usuário deletado
c.JSON(http.StatusNoContent, nil)
}
Cada anotação tem um propósito específico:
@Summarye@Description: explicam o que o endpoint faz@Tags: agrupam endpoints relacionados na interface@Param: documentam parâmetros (path, query, body)@Successe@Failure: documentam respostas possíveis com seus status codes e estruturas@Router: define o caminho e método HTTP@Security: indica que o endpoint requer autenticação
Gerando a Documentação e Acessando a Interface
Após anotar seu código, execute o swaggo para gerar a especificação OpenAPI:
swag init
Esse comando procura por anotações em seu projeto (começando por main.go por padrão) e cria um diretório docs/ com arquivos YAML e JSON que descrevem sua API. Você verá uma mensagem de sucesso indicando que o arquivo swagger.yaml foi gerado.
Agora execute sua aplicação:
go run main.go
Acesse http://localhost:8080/swagger/index.html em seu navegador. Você verá a interface Swagger UI — uma página interativa e bonita onde pode explorar todos os seus endpoints, ver seus parâmetros, testar requisições diretamente da interface, e visualizar respostas reais.
Regenerando Documentação após Alterações
Toda vez que você alterar anotações ou adicionar novos endpoints, execute swag init novamente. Para desenvolvimento contínuo, você pode instalar uma ferramenta como air para recompilar automaticamente:
go install github.com/cosmtrek/air@latest
air
Isso executará swag init a cada mudança nos arquivos, mantendo a documentação sempre sincronizada com seu código.
Boas Práticas e Padrões Avançados
Versionamento de API
É uma prática saudável versionamento de API. A rota /api/v1 em nosso exemplo já segue esse padrão. Se você precisar fazer mudanças incompatíveis no futuro, pode manter /api/v1 para clientes antigos e criar /api/v2 com as novas mudanças, sem quebrar ninguém.
Documentação de Respostas de Erro
Sempre documente explicitamente quais erros um endpoint pode retornar. Isso ajuda consumidores a lidar com falhas corretamente:
// @Failure 400 {object} ErrorResponse "Erro de validação"
// @Failure 401 {object} ErrorResponse "Não autenticado"
// @Failure 403 {object} ErrorResponse "Sem permissão"
// @Failure 500 {object} ErrorResponse "Erro interno do servidor"
Defina um tipo ErrorResponse claro em seus modelos:
type ErrorResponse struct {
Code int `json:"code" example:"400"`
Message string `json:"message" example:"Email já existe"`
Details string `json:"details,omitempty" example:"O campo 'email' deve ser único"`
}
Documentando Query Parameters e Headers
Se seu endpoint aceita filtros ou parâmetros opcionais:
// @Param limit query int false "Número máximo de resultados" default(10)
// @Param offset query int false "Número de registros a pular" default(0)
// @Param sort query string false "Campo para ordenação" Enums(id,name,email)
Para headers customizados:
// @Param X-Request-ID header string false "ID único da requisição"
Autenticação e Segurança
Já mencionamos @Security Bearer nas anotações. Você pode definir múltiplos esquemas de segurança globalmente e aplicar a endpoints específicos:
// @securityDefinitions.apikey ApiKeyAuth
// @in header
// @name X-API-Key
// @securityDefinitions.basic BasicAuth
Depois, em cada endpoint, especifique qual segurança se aplica:
// @Security ApiKeyAuth
// @Security BasicAuth
Conclusão
A documentação de APIs é tão importante quanto o código que as implementa. O Swagger e swaggo resolvem o problema clássico de documentação desatualizada, permitindo que você mantenha a especificação viva no seu código-fonte. Primeiro aprendizado: anotações no código são manutenção zero de documentação — você muda o código, atualiza a anotação ali mesmo, executa swag init, e pronto.
Segundo ponto-chave: a interface Swagger UI transforma especificação técnica em experiência visual. Desenvolvedores podem explorar sua API interativamente, testar endpoints sem escrever curl, entender estruturas de dados rapidamente. Isso reduz drasticamente o tempo de onboarding e diminui bugs de integração causados por desentendimentos sobre a API.
Por fim, swaggo não adiciona overhead ao seu projeto — trata-se apenas de geração de arquivos estáticos (YAML/JSON), não de middleware ou complexidade em runtime. A documentação é gerada uma única vez durante desenvolvimento e pode ser servida estaticamente em produção, sem custo de performance.