Go

O que Todo Dev Deve Saber sobre Documentação de APIs Go com Swagger e swaggo

16 min de leitura

O que Todo Dev Deve Saber sobre Documentação de APIs Go com Swagger e swaggo

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

<h2>Entendendo APIs e a Importância da Documentação</h2>

<p>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.</p>

<p>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.</p>

<h2>O que é Swagger/OpenAPI e por que usar swaggo</h2>

<p>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.</p>

<p>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.</p>

<h3>Instalação e Configuração Inicial</h3>

<p>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.</p>

<pre><code class="language-bash">go install github.com/swaggo/swag/cmd/swag@latest</code></pre>

<p>Em seguida, inicialize um módulo Go e adicione as dependências necessárias:</p>

<pre><code class="language-bash">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</code></pre>

<p>Crie a estrutura básica do seu projeto:</p>

<pre><code>seu-projeto/

├── main.go

├── handlers/

│ └── users.go

├── models/

│ └── user.go

└── go.mod</code></pre>

<h2>Estruturando sua API com anotações Swagger</h2>

<p>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.</p>

<h3>Anotações Globais da API</h3>

<p>Comece definindo informações globais sobre sua API no arquivo main.go:</p>

<pre><code class="language-go">package main

import (

&quot;github.com/gin-gonic/gin&quot;

swaggerFiles &quot;github.com/swaggo/files&quot;

ginSwagger &quot;github.com/swaggo/gin-swagger&quot;

_ &quot;github.com/seu-usuario/sua-api/docs&quot;

)

// @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(&quot;/swagger/*any&quot;, ginSwagger.WrapHandler(swaggerFiles.Handler))

// Suas rotas de API aqui

api := router.Group(&quot;/api/v1&quot;)

{

api.POST(&quot;/users&quot;, CreateUser)

api.GET(&quot;/users/:id&quot;, GetUser)

api.PUT(&quot;/users/:id&quot;, UpdateUser)

api.DELETE(&quot;/users/:id&quot;, DeleteUser)

}

router.Run(&quot;:8080&quot;)

}</code></pre>

<p>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 <code>_ &quot;github.com/seu-usuario/sua-api/docs&quot;</code> é importante — ele carrega o pacote docs gerado pelo swaggo.</p>

<h3>Modelos de Dados com Tags Swagger</h3>

<p>Defina seus modelos de dados em <code>models/user.go</code>:</p>

<pre><code class="language-go">package models

// User representa um usuário no sistema

type User struct {

// ID único do usuário

// example: 1

ID int json:&quot;id&quot; example:&quot;1&quot;

// Nome completo do usuário

// example: João Silva

Name string json:&quot;name&quot; example:&quot;João Silva&quot;

// Email do usuário (deve ser único)

// example: joao@example.com

Email string json:&quot;email&quot; example:&quot;joao@example.com&quot;

// Idade do usuário

// example: 30

Age int json:&quot;age&quot; example:&quot;30&quot;

}

// 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:&quot;name&quot; binding:&quot;required,min=3,max=100&quot; example:&quot;Maria Santos&quot;

// Email do usuário (obrigatório)

Email string json:&quot;email&quot; binding:&quot;required,email&quot; example:&quot;maria@example.com&quot;

// Idade do usuário (obrigatório)

// minimum: 18

// maximum: 120

Age int json:&quot;age&quot; binding:&quot;required,min=18,max=120&quot; example:&quot;25&quot;

}</code></pre>

<p>Repare que não há tags especiais <code>swagger:</code> aqui. O swaggo é inteligente o suficiente para ler os tags JSON e os comentários acima dos campos.</p>

<h3>Endpoints com Documentação Detalhada</h3>

<p>Agora documente seus handlers em <code>handlers/users.go</code>:</p>

<pre><code class="language-go">package handlers

import (

&quot;net/http&quot;

&quot;strconv&quot;

&quot;github.com/gin-gonic/gin&quot;

&quot;github.com/seu-usuario/sua-api/models&quot;

)

// 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 &quot;Dados do usuário a ser criado&quot;

// @Success 201 {object} models.User &quot;Usuário criado com sucesso&quot;

// @Failure 400 {object} map[string]string &quot;Erro de validação&quot;

// @Failure 500 {object} map[string]string &quot;Erro interno do servidor&quot;

// @Router /users [post]

// @Security Bearer

func CreateUser(c *gin.Context) {

var req models.CreateUserRequest

if err := c.ShouldBindJSON(&amp;req); err != nil {

c.JSON(http.StatusBadRequest, gin.H{&quot;error&quot;: 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 &quot;ID do usuário&quot;

// @Success 200 {object} models.User &quot;Usuário encontrado&quot;

// @Failure 404 {object} map[string]string &quot;Usuário não encontrado&quot;

// @Router /users/{id} [get]

// @Security Bearer

func GetUser(c *gin.Context) {

id, err := strconv.Atoi(c.Param(&quot;id&quot;))

if err != nil {

c.JSON(http.StatusBadRequest, gin.H{&quot;error&quot;: &quot;ID inválido&quot;})

return

}

// Simulação: retornar usuário

user := models.User{

ID: id,

Name: &quot;João Silva&quot;,

Email: &quot;joao@example.com&quot;,

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 &quot;ID do usuário&quot;

// @Param request body models.CreateUserRequest true &quot;Dados a serem atualizados&quot;

// @Success 200 {object} models.User &quot;Usuário atualizado&quot;

// @Failure 400 {object} map[string]string &quot;Erro de validação&quot;

// @Failure 404 {object} map[string]string &quot;Usuário não encontrado&quot;

// @Router /users/{id} [put]

// @Security Bearer

func UpdateUser(c *gin.Context) {

id, err := strconv.Atoi(c.Param(&quot;id&quot;))

if err != nil {

c.JSON(http.StatusBadRequest, gin.H{&quot;error&quot;: &quot;ID inválido&quot;})

return

}

var req models.CreateUserRequest

if err := c.ShouldBindJSON(&amp;req); err != nil {

c.JSON(http.StatusBadRequest, gin.H{&quot;error&quot;: 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 &quot;ID do usuário&quot;

// @Success 204 &quot;&quot;

// @Failure 404 {object} map[string]string &quot;Usuário não encontrado&quot;

// @Router /users/{id} [delete]

// @Security Bearer

func DeleteUser(c *gin.Context) {

id, err := strconv.Atoi(c.Param(&quot;id&quot;))

if err != nil {

c.JSON(http.StatusBadRequest, gin.H{&quot;error&quot;: &quot;ID inválido&quot;})

return

}

// Simulação: usuário deletado

c.JSON(http.StatusNoContent, nil)

}</code></pre>

<p>Cada anotação tem um propósito específico:</p>

<ul>

<li><code>@Summary</code> e <code>@Description</code>: explicam o que o endpoint faz</li>

<li><code>@Tags</code>: agrupam endpoints relacionados na interface</li>

<li><code>@Param</code>: documentam parâmetros (path, query, body)</li>

<li><code>@Success</code> e <code>@Failure</code>: documentam respostas possíveis com seus status codes e estruturas</li>

<li><code>@Router</code>: define o caminho e método HTTP</li>

<li><code>@Security</code>: indica que o endpoint requer autenticação</li>

</ul>

<h2>Gerando a Documentação e Acessando a Interface</h2>

<p>Após anotar seu código, execute o swaggo para gerar a especificação OpenAPI:</p>

<pre><code class="language-bash">swag init</code></pre>

<p>Esse comando procura por anotações em seu projeto (começando por <code>main.go</code> por padrão) e cria um diretório <code>docs/</code> com arquivos YAML e JSON que descrevem sua API. Você verá uma mensagem de sucesso indicando que o arquivo <code>swagger.yaml</code> foi gerado.</p>

<p>Agora execute sua aplicação:</p>

<pre><code class="language-bash">go run main.go</code></pre>

<p>Acesse <code>http://localhost:8080/swagger/index.html</code> 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.</p>

<h3>Regenerando Documentação após Alterações</h3>

<p>Toda vez que você alterar anotações ou adicionar novos endpoints, execute <code>swag init</code> novamente. Para desenvolvimento contínuo, você pode instalar uma ferramenta como <code>air</code> para recompilar automaticamente:</p>

<pre><code class="language-bash">go install github.com/cosmtrek/air@latest

air</code></pre>

<p>Isso executará <code>swag init</code> a cada mudança nos arquivos, mantendo a documentação sempre sincronizada com seu código.</p>

<h2>Boas Práticas e Padrões Avançados</h2>

<h3>Versionamento de API</h3>

<p>É uma prática saudável versionamento de API. A rota <code>/api/v1</code> em nosso exemplo já segue esse padrão. Se você precisar fazer mudanças incompatíveis no futuro, pode manter <code>/api/v1</code> para clientes antigos e criar <code>/api/v2</code> com as novas mudanças, sem quebrar ninguém.</p>

<h3>Documentação de Respostas de Erro</h3>

<p>Sempre documente explicitamente quais erros um endpoint pode retornar. Isso ajuda consumidores a lidar com falhas corretamente:</p>

<pre><code class="language-go">// @Failure 400 {object} ErrorResponse &quot;Erro de validação&quot;

// @Failure 401 {object} ErrorResponse &quot;Não autenticado&quot;

// @Failure 403 {object} ErrorResponse &quot;Sem permissão&quot;

// @Failure 500 {object} ErrorResponse &quot;Erro interno do servidor&quot;</code></pre>

<p>Defina um tipo <code>ErrorResponse</code> claro em seus modelos:</p>

<pre><code class="language-go">type ErrorResponse struct {

Code int json:&quot;code&quot; example:&quot;400&quot;

Message string json:&quot;message&quot; example:&quot;Email já existe&quot;

Details string json:&quot;details,omitempty&quot; example:&quot;O campo &#039;email&#039; deve ser único&quot;

}</code></pre>

<h3>Documentando Query Parameters e Headers</h3>

<p>Se seu endpoint aceita filtros ou parâmetros opcionais:</p>

<pre><code class="language-go">// @Param limit query int false &quot;Número máximo de resultados&quot; default(10)

// @Param offset query int false &quot;Número de registros a pular&quot; default(0)

// @Param sort query string false &quot;Campo para ordenação&quot; Enums(id,name,email)</code></pre>

<p>Para headers customizados:</p>

<pre><code class="language-go">// @Param X-Request-ID header string false &quot;ID único da requisição&quot;</code></pre>

<h3>Autenticação e Segurança</h3>

<p>Já mencionamos <code>@Security Bearer</code> nas anotações. Você pode definir múltiplos esquemas de segurança globalmente e aplicar a endpoints específicos:</p>

<pre><code class="language-go">// @securityDefinitions.apikey ApiKeyAuth

// @in header

// @name X-API-Key

// @securityDefinitions.basic BasicAuth</code></pre>

<p>Depois, em cada endpoint, especifique qual segurança se aplica:</p>

<pre><code class="language-go">// @Security ApiKeyAuth

// @Security BasicAuth</code></pre>

<h2>Conclusão</h2>

<p>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: <strong>anotações no código são manutenção zero de documentação</strong> — você muda o código, atualiza a anotação ali mesmo, executa <code>swag init</code>, e pronto.</p>

<p>Segundo ponto-chave: <strong>a interface Swagger UI transforma especificação técnica em experiência visual</strong>. 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.</p>

<p>Por fim, <strong>swaggo não adiciona overhead ao seu projeto</strong> — 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.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://swagger.io/specification/" target="_blank" rel="noopener noreferrer">Documentação Oficial do Swagger/OpenAPI</a></li>

<li><a href="https://github.com/swaggo/swag" target="_blank" rel="noopener noreferrer">Repositório swaggo no GitHub</a></li>

<li><a href="https://gin-gonic.com/docs/" target="_blank" rel="noopener noreferrer">Documentação Gin Framework</a></li>

<li><a href="https://spec.openapis.org/oas/v3.0.3" target="_blank" rel="noopener noreferrer">OpenAPI 3.0 Specification</a></li>

<li><a href="https://swagger.io/resources/articles/best-practices-in-api-documentation/" target="_blank" rel="noopener noreferrer">Best Practices for REST API Documentation</a></li>

</ul>

<p>&lt;!-- FIM --&gt;</p>

Comentários

Mais em Go

Dominando Arrays, Slices e Maps em Go: A Base das Coleções em Projetos Reais
Dominando Arrays, Slices e Maps em Go: A Base das Coleções em Projetos Reais

Arrays: A Estrutura Fixa de Dados Arrays são coleções de tamanho fixo que arm...

Guia Completo de Testes de Integração em Go: Banco Real com testcontainers-go
Guia Completo de Testes de Integração em Go: Banco Real com testcontainers-go

Por que Testes de Integração Importam em Go Testes de integração são diferent...

Autenticação JWT em APIs Go: Geração, Validação e Refresh Tokens: Do Básico ao Avançado
Autenticação JWT em APIs Go: Geração, Validação e Refresh Tokens: Do Básico ao Avançado

Entendendo JWT: Fundamentos e Estrutura JSON Web Token (JWT) é um padrão aber...