- Por que a documentação morre antes da API
- O que é OpenAPI e o que é Swagger, afinal
- Estrutura mínima de um arquivo OpenAPI
- Swagger no Node.js com Express
- Swagger no NestJS com decoradores
- Erros comuns que vão te fazer perder tempo
- Versionamento e múltiplos ambientes
- FAQ
- Próximos passos
Por que a documentação morre antes da API
Se você já pegou um Postman collection desatualizado de um colega e passou meia hora descobrindo que o campo user_id agora se chama userId, você sabe do que estou falando. Documentação de API que vive separada do código é documentação que vai mentir pra você na pior hora possível — geralmente na véspera de uma entrega.
O problema não é falta de esforço. É arquitetura errada. Quando a doc fica num Notion, num README ou num Confluence que ninguém lembra de atualizar, ela envelhece sozinha. A solução é simples na teoria: documentação acoplada ao código, gerada automaticamente, versionada junto com a API.
É exatamente isso que o ecossistema OpenAPI/Swagger resolve.
O que é OpenAPI e o que é Swagger, afinal
A confusão entre os dois nomes é clássica. OpenAPI é a especificação — um padrão aberto (atualmente na versão 3.1) que define como descrever uma API REST em YAML ou JSON. Swagger era o nome original do projeto, que foi doado para a Linux Foundation e renomeado. Hoje, "Swagger" se refere às ferramentas: Swagger UI, Swagger Editor, Swagger Codegen.
Na prática: você escreve (ou gera) um arquivo OpenAPI, e o Swagger UI transforma isso numa interface interativa onde qualquer pessoa pode ver e testar os endpoints.
💡 Dica: A versão 3.0+ do OpenAPI é bem diferente da 2.0 (ainda chamada de "Swagger"). Se você está começando hoje, vá direto pra 3.x e economize dor de cabeça futura.
Estrutura mínima de um arquivo OpenAPI
Antes de integrar com qualquer framework, vale entender o que você está gerando. Um arquivo OpenAPI 3.0 tem esta estrutura básica:
openapi: 3.0.3
info:
title: Minha API
version: 1.0.0
description: API para gerenciar usuários
servers:
- url: https://api.exemplo.com/v1
- url: http://localhost:3000/v1
paths:
/users:
get:
summary: Lista todos os usuários
tags:
- Users
parameters:
- name: page
in: query
schema:
type: integer
default: 1
responses:
'200':
description: Lista paginada de usuários
content:
application/json:
schema:
$ref: '#/components/schemas/UserList'
'401':
description: Não autorizado
components:
schemas:
User:
type: object
required:
- id
- email
properties:
id:
type: string
format: uuid
email:
type: string
format: email
name:
type: string
UserList:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/User'
total:
type: integer
page:
type: integer
O bloco components/schemas é onde você define seus modelos reutilizáveis. Usar $ref em vez de repetir a estrutura em todo endpoint é o que separa uma documentação sustentável de um pesadelo de manutenção.
Swagger no Node.js com Express
A abordagem mais direta com Express usa a lib swagger-jsdoc para gerar o spec a partir de comentários JSDoc, e swagger-ui-express para servir a interface.
npm install swagger-jsdoc swagger-ui-express
npm install -D @types/swagger-jsdoc @types/swagger-ui-express
Configure na entrada da aplicação:
// src/swagger.ts
import swaggerJsdoc from 'swagger-jsdoc';
const options = {
definition: {
openapi: '3.0.3',
info: {
title: 'Minha API',
version: '1.0.0',
},
servers: [{ url: process.env.API_URL || 'http://localhost:3000' }],
components: {
securitySchemes: {
bearerAuth: {
type: 'http',
scheme: 'bearer',
bearerFormat: 'JWT',
},
},
},
},
apis: ['./src/routes/**/*.ts'], // onde ficam seus comentários
};
export const swaggerSpec = swaggerJsdoc(options);
// src/app.ts
import express from 'express';
import swaggerUi from 'swagger-ui-express';
import { swaggerSpec } from './swagger';
const app = express();
app.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));
E nos seus arquivos de rota:
// src/routes/users.ts
/**
* @openapi
* /users:
* get:
* tags:
* - Users
* summary: Lista usuários paginados
* security:
* - bearerAuth: []
* parameters:
* - name: page
* in: query
* schema:
* type: integer
* default: 1
* responses:
* 200:
* description: OK
* 401:
* description: Token inválido ou ausente
*/
router.get('/users', authMiddleware, usersController.list);
⚠️ Atenção: Manter comentários JSDoc sincronizados com a lógica real do controller é a parte que exige disciplina. Se você muda o comportamento do endpoint e esquece de atualizar o comentário, voltamos ao problema inicial. Considere adicionar testes que validam o contrato OpenAPI.
Swagger no NestJS com decoradores
O NestJS tem integração nativa e bem mais ergonômica. A experiência aqui é bem diferente — você decora seus DTOs e controllers, e o framework gera o spec automaticamente.
npm install @nestjs/swagger swagger-ui-express
// main.ts
import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const config = new DocumentBuilder()
.setTitle('Minha API')
.setVersion('1.0')
.addBearerAuth()
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('docs', app, document);
await app.listen(3000);
}
bootstrap();
Decorando o DTO:
// create-user.dto.ts
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { IsEmail, IsString, MinLength } from 'class-validator';
export class CreateUserDto {
@ApiProperty({ example: 'joao@exemplo.com' })
@IsEmail()
email: string;
@ApiProperty({ example: 'João Silva' })
@IsString()
name: string;
@ApiPropertyOptional({ example: 'admin' })
role?: string;
}
E no controller:
@ApiTags('Users')
@ApiBearerAuth()
@Controller('users')
export class UsersController {
@Post()
@ApiOperation({ summary: 'Cria um novo usuário' })
@ApiResponse({ status: 201, description: 'Usuário criado', type: UserEntity })
@ApiResponse({ status: 409, description: 'Email já cadastrado' })
create(@Body() dto: CreateUserDto) {
return this.usersService.create(dto);
}
}
Essa abordagem ganha muito no NestJS porque os decoradores vivem no mesmo arquivo que a lógica. Ficou mais difícil de esquecer de atualizar.
Erros comuns que vão te fazer perder tempo
1. Não documentar os erros
Isso aconteceu num PR que revisei: o endpoint tinha só 200 documentado. Em produção, ele retornava 400, 401, 403 e 422 dependendo do contexto. O frontend ficou tratando tudo como erro genérico por semanas.
❌ Documentar só o caminho feliz:
responses:
'200':
description: OK
✓ Documentar os casos reais:
responses:
'200':
description: Usuário criado com sucesso
'400':
description: Dados inválidos no corpo da requisição
'409':
description: Email já está em uso
'422':
description: Entidade não processável — erro de validação detalhado
2. Repetir schemas em vez de usar $ref
Se você descreve o objeto User em cinco endpoints diferentes, na primeira vez que mudar um campo você vai esquecer de atualizar pelo menos um deles. Use $ref pra tudo que se repete.
3. Não usar example nos campos
Um schema sem exemplos força quem está lendo a adivinhar o formato esperado. Adicionar example: "2026-01-15T10:30:00Z" num campo de data evita uma série de e-mails desnecessários.
4. Não versionar a documentação junto com o código
Se o spec fica fora do repositório, ele vai ficar desatualizado. Commitar o openapi.yaml junto com o código garante que qualquer PR que muda um endpoint também muda a documentação — e o reviewer vai cobrar.
Versionamento e múltiplos ambientes
Uma situação comum: sua API tem v1 e v2 em produção ao mesmo tempo, mais o ambiente de staging. Como servir docs separadas?
// Gere um spec por versão
const v1Document = SwaggerModule.createDocument(app, configV1, {
include: [UsersV1Module, AuthModule],
});
const v2Document = SwaggerModule.createDocument(app, configV2, {
include: [UsersV2Module, AuthModule],
});
SwaggerModule.setup('docs/v1', app, v1Document);
SwaggerModule.setup('docs/v2', app, v2Document);
Para os ambientes, use a propriedade servers do spec para listar staging e produção. O Swagger UI deixa o usuário selecionar qual servidor usar diretamente na interface — o que elimina a necessidade de ter instâncias separadas de documentação.
const config = new DocumentBuilder()
.setTitle('Minha API')
.addServer('https://api.meusite.com', 'Produção')
.addServer('https://staging-api.meusite.com', 'Staging')
.addServer('http://localhost:3000', 'Local')
.build();
FAQ
Preciso escrever o OpenAPI à mão ou posso gerar automaticamente?
Depende do stack. No NestJS você praticamente não escreve YAML à mão — os decoradores geram tudo. No Express com swagger-jsdoc, você escreve em comentários. Se você tem uma API legada sem nenhuma integração, ferramentas como swagger-autogen conseguem inferir endpoints existentes como ponto de partida, mas o resultado vai precisar de revisão manual.
Dá pra usar o OpenAPI spec pra gerar o client do frontend automaticamente?
Sim, e isso é uma das maiores vantagens. Ferramentas como openapi-typescript-codegen e orval leem seu spec e geram tipos TypeScript e até hooks React prontos. Seu frontend para de manter types na mão e passa a consumir o contrato da API diretamente.
Como protejo a rota /docs em produção?
O jeito mais simples é checar a variável de ambiente e só registrar a rota fora de produção. Se precisar liberar pra parceiros específicos, adicione um middleware de autenticação básica (HTTP Basic Auth) na frente do Swagger UI. Nunca exponha a documentação sem autenticação numa API que tem dados sensíveis.
O Swagger UI é pesado. Posso usar outra interface?
Sim. O spec OpenAPI é um padrão aberto — você não está preso ao Swagger UI. Alternativas como Redoc, Scalar e Stoplight Elements leem o mesmo arquivo e entregam interfaces mais limpas e modernas. O Scalar, em especial, tem ganhado bastante adoção por ter uma UI muito mais agradável que o Swagger UI padrão.
Como valido que minha implementação está de acordo com o spec?
Existe a biblioteca express-openapi-validator que faz exatamente isso: valida as requests e responses em runtime contra o seu spec. É especialmente útil em testes de integração — você garante que nenhum endpoint está divergindo do contrato documentado.
Próximos passos
Se você está começando agora, o caminho mais direto é:
- Escolha a abordagem certa pro seu stack — NestJS tem a melhor DX, Express com jsdoc é mais flexível, YAML puro dá mais controle.
- Commite o spec gerado no repositório — inclua no CI uma etapa que valida o arquivo OpenAPI com
swagger-cli validate. - Documente os erros desde o primeiro endpoint — não deixe pra depois. Depois nunca chega.
- Explore a geração de clients — se você tem frontend TypeScript, testar o
orvalvai te poupar horas de manutenção de tipos. - Considere o Scalar como alternativa ao Swagger UI — a curva de adoção é zero, a interface é bem melhor.
Documentação de API não precisa ser uma tarefa separada que ninguém quer fazer. Quando ela vive junto com o código e é gerada automaticamente, ela simplesmente acontece — e para de ser o arquivo que todo mundo abre desconfiado de que está desatualizado.