OpenAPI/Swagger em Node.js: documentação automática
OpenAPI (Swagger) é o padrão para documentar APIs REST — descreve todos os endpoints, parâmetros, schemas de request/response e autenticação em um arquivo JSON/YAML. Com as bibliotecas certas, a documentação é gerada automaticamente do código e fica sempre sincronizada, eliminando docs desatualizados.
Configurar swagger-jsdoc e swagger-ui-express
Gerar documentação OpenAPI a partir de comentários JSDoc:
// npm install swagger-jsdoc swagger-ui-express
// npm install -D @types/swagger-jsdoc @types/swagger-ui-express
import swaggerJsdoc from 'swagger-jsdoc'
import swaggerUi from 'swagger-ui-express'
const swaggerOptions: swaggerJsdoc.Options = {
definition: {
openapi: '3.1.0',
info: {
title: 'Minha API',
version: '2.0.0',
description: 'API de gerenciamento de pedidos',
contact: {
name: 'Suporte',
email: 'api@seudominio.com.br',
},
},
servers: [
{ url: 'https://api.seudominio.com.br', description: 'Produção' },
{ url: 'http://localhost:3000', description: 'Desenvolvimento' },
],
components: {
securitySchemes: {
bearerAuth: {
type: 'http',
scheme: 'bearer',
bearerFormat: 'JWT',
},
apiKey: {
type: 'apiKey',
in: 'header',
name: 'X-API-Key',
},
},
},
security: [{ bearerAuth: [] }], // segurança padrão para todos os endpoints
},
apis: ['./src/routes/**/*.ts', './src/schemas/*.ts'], // arquivos com anotações
}
const swaggerSpec = swaggerJsdoc(swaggerOptions)
// Servir documentação interativa:
app.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec, {
customSiteTitle: 'Minha API — Docs',
swaggerOptions: {
persistAuthorization: true, // manter token entre reloads
},
}))
// Expor o spec JSON para ferramentas:
app.get('/docs/openapi.json', (req, res) => res.json(swaggerSpec))Documentar endpoints com JSDoc
Anotar rotas com comentários OpenAPI:
// src/routes/v2/pedidos.ts
/**
* @swagger
* /api/v2/pedidos:
* get:
* summary: Listar pedidos do usuário autenticado
* tags: [Pedidos]
* security:
* - bearerAuth: []
* parameters:
* - in: query
* name: status
* schema:
* type: string
* enum: [pendente, aprovado, cancelado]
* description: Filtrar por status
* - in: query
* name: page
* schema:
* type: integer
* default: 1
* - in: query
* name: limit
* schema:
* type: integer
* default: 20
* maximum: 100
* responses:
* 200:
* description: Lista de pedidos
* content:
* application/json:
* schema:
* type: object
* properties:
* pedidos:
* type: array
* items:
* $ref: '#/components/schemas/Pedido'
* total:
* type: integer
* 401:
* $ref: '#/components/responses/NaoAutorizado'
*/
router.get('/', autenticar, listarPedidos)
/**
* @swagger
* /api/v2/pedidos:
* post:
* summary: Criar novo pedido
* tags: [Pedidos]
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/CriarPedidoInput'
* responses:
* 201:
* description: Pedido criado
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Pedido'
* 400:
* $ref: '#/components/responses/EntradaInvalida'
*/
router.post('/', autenticar, criarPedido)Definir schemas com Zod e gerar OpenAPI
Usar zod-to-openapi para gerar specs do Zod automaticamente:
// npm install @asteasolutions/zod-to-openapi zod
import { z } from 'zod'
import { extendZodWithOpenApi, OpenApiGeneratorV31, OpenAPIRegistry } from '@asteasolutions/zod-to-openapi'
extendZodWithOpenApi(z)
const registry = new OpenAPIRegistry()
// Definir schema com Zod (com anotações OpenAPI):
const PedidoSchema = registry.register(
'Pedido',
z.object({
id: z.string().uuid().openapi({ description: 'ID único do pedido' }),
status: z.enum(['pendente', 'aprovado', 'cancelado']).openapi({ example: 'pendente' }),
valor_total: z.number().positive().openapi({ description: 'Valor total em reais', example: 150.00 }),
moeda: z.string().length(3).default('BRL').openapi({ example: 'BRL' }),
criado_em: z.string().datetime().openapi({ description: 'Data de criação ISO 8601' }),
}).openapi('Pedido')
)
const CriarPedidoInputSchema = registry.register(
'CriarPedidoInput',
z.object({
itens: z.array(z.object({
produto_id: z.string().uuid(),
quantidade: z.number().int().min(1),
})).min(1).openapi({ description: 'Lista de itens do pedido' }),
observacao: z.string().max(500).optional(),
}).openapi('CriarPedidoInput')
)
// Registrar endpoint:
registry.registerPath({
method: 'get',
path: '/api/v2/pedidos',
summary: 'Listar pedidos',
tags: ['Pedidos'],
security: [{ bearerAuth: [] }],
responses: {
200: {
description: 'Lista de pedidos',
content: {
'application/json': {
schema: z.object({ pedidos: z.array(PedidoSchema) }),
},
},
},
},
})
// Gerar spec:
const generator = new OpenApiGeneratorV31(registry.definitions)
const spec = generator.generateDocument({
openapi: '3.1.0',
info: { title: 'Minha API', version: '2.0.0' },
})Validação automática de request com spec OpenAPI
Validar inputs contra a spec OpenAPI usando express-openapi-validator:
// npm install express-openapi-validator
import OpenApiValidator from 'express-openapi-validator'
// Configurar validador antes das rotas:
app.use(
OpenApiValidator.middleware({
apiSpec: './openapi.json', // ou o objeto spec gerado
validateRequests: true, // validar body, query, params
validateResponses: false, // opcional: validar respostas (desenvolvimento)
ignorePaths: /\/docs/, // não validar a rota de docs
})
)
// Handler de erros de validação:
app.use((err: unknown, req: Request, res: Response, next: NextFunction) => {
if ((err as { status?: number }).status === 400) {
return res.status(400).json({
error: 'Entrada inválida',
detalhes: (err as { errors?: unknown[] }).errors,
})
}
next(err)
})
// Agora o middleware valida automaticamente:
// POST /api/v2/pedidos sem "itens" → 400 com detalhes do erro
// GET /api/v2/pedidos?status=invalido → 400 (não está no enum)
// Tip: testar a spec com ferramentas CLI:
// npx openapi-validator openapi.json
// npx @stoplight/spectral-cli lint openapi.jsonGerar clients TypeScript automaticamente
Usar openapi-ts para gerar clients type-safe a partir da spec:
# Gerar client TypeScript para o frontend a partir da spec:
# npm install -D @hey-api/openapi-ts
# package.json:
# "generate:client": "openapi-ts -i http://localhost:3000/docs/openapi.json -o src/api-client"
npx @hey-api/openapi-ts \
--input http://localhost:3000/docs/openapi.json \
--output src/api-client \
--client fetch
# Resultado: src/api-client/
# types.gen.ts → tipos TypeScript de todos os schemas
# services.gen.ts → funções de chamada type-safe
# index.ts
# Usar no frontend React:
# import { PedidosService } from '@/api-client'
# const pedidos = await PedidosService.listarPedidos({ status: 'pendente' })
# pedidos.data.pedidos[0].valor_total ← TypeScript sabe o tipo!
# Alternativas:
# openapi-generator-cli: gera para múltiplas linguagens (Java, Python, Go...)
# swagger-typescript-api: templates customizáveis
# orval: geração com React Query hooks integrados
# No CI/CD: garantir que spec e client estão sincronizados:
# 1. npm run generate:client
# 2. git diff --exit-code src/api-client/ || (echo "Client desatualizado!" && exit 1)$ runstack deploy --plan starter
Não quer configurar manualmente?
Não quer configurar manualmente? Implante o VPS para APIs em menos de 3 minutos com a Runstack. Infraestrutura da OPEN DATACENTER, com servidores no Brasil.
Perguntas frequentes
Conteúdos relacionados