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:

    typescript
    // 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:

    typescript
    // 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:

    typescript
    // 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:

    typescript
    // 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.json

    Gerar clients TypeScript automaticamente

    Usar openapi-ts para gerar clients type-safe a partir da spec:

    bash
    # 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