AzuraJS Logo
AzuraJSFramework
v2.2 Beta

Swagger / OpenAPI

Documentação completa de API com interface moderna e interativa

Swagger / OpenAPI 3.0

AzuraJS inclui suporte completo para documentação de API usando Swagger/OpenAPI 3.0, com uma interface moderna dark mode e funcionalidade "Try It Out" integrada.

Nota: O sistema Swagger do AzuraJS gera automaticamente documentação OpenAPI 3.0 a partir dos seus controllers e decorators, sem configuração manual.

Instalação

O Swagger já vem incluído no AzuraJS. Basta importar do submódulo azurajs/swagger:

import { setupSwaggerWithControllers } from "azurajs/swagger";

Setup Rápido

A forma mais simples de adicionar Swagger à sua aplicação é com uma linha:

import { AzuraClient } from "azurajs";
import { setupSwaggerWithControllers } from "azurajs/swagger";
import { UserController } from "./controllers/UserController";

const app = new AzuraClient();

// Uma linha para configurar tudo!
setupSwaggerWithControllers(app, {
  title: "My API",
  description: "API documentation",
  version: "1.0.0"
}, [UserController]);

app.listen();

Acesse a documentação em:

  • UI Interativa: http://localhost:3000/docs
  • OpenAPI JSON: http://localhost:3000/api-spec.json

Decorators de Documentação

@Swagger() - Nova API Simplificada ⭐

O decorator @Swagger unifica toda a documentação em um único lugar, tornando muito mais fácil e intuitivo documentar suas APIs:

import { Controller, Get, Post } from "azurajs/decorators";
import { Swagger } from "azurajs/swagger";

@Controller("/users")
class UserController {
  @Get("/:id")
  @Swagger({
    summary: "Busca usuário por ID",
    description: "Retorna informações detalhadas de um usuário específico",
    tags: ["Users"],
    parameters: [
      {
        name: "id",
        in: "path",
        description: "ID do usuário",
        required: true,
        schema: { type: "string" },
        example: "123"
      }
    ],
    responses: {
      200: {
        description: "Usuário encontrado",
        example: { 
          id: "123", 
          name: "John Doe",
          email: "[email protected]"
        }
      },
      404: {
        description: "Usuário não encontrado",
        example: { error: "User not found" }
      }
    }
  })
  async getUser(@Req() req, @Res() res) {
    res.json({ id: req.params.id, name: "John Doe" });
  }

  @Post("/")
  @Swagger({
    summary: "Cria novo usuário",
    tags: ["Users"],
    requestBody: {
      description: "Dados do usuário",
      required: true,
      content: {
        "application/json": {
          schema: {
            type: "object",
            properties: {
              name: { type: "string" },
              email: { type: "string", format: "email" }
            },
            required: ["name", "email"]
          },
          example: {
            name: "Jane Doe",
            email: "[email protected]"
          }
        }
      }
    },
    responses: {
      201: {
        description: "Usuário criado com sucesso",
        example: { 
          id: 456, 
          name: "Jane Doe",
          email: "[email protected]"
        }
      },
      400: {
        description: "Dados inválidos",
        example: { error: "Invalid data" }
      }
    }
  })
  async createUser(@Req() req, @Res() res) {
    res.status(201).json(req.body);
  }
}

Vantagens do @Swagger:

  • Tudo em um lugar - Não precisa usar múltiplos decorators
  • Mais legível - Configuração agrupada e organizada
  • Menos código - Uma única declaração ao invés de 4-5 decorators
  • TypeScript - Autocompletar e validação de tipos
  • Exemplos integrados - Defina exemplos direto nas responses

Decorators Individuais (API Tradicional)

Se preferir maior granularidade, você ainda pode usar os decorators individuais:

@ApiDoc()

Documenta informações básicas do endpoint:

import { Controller, Get } from "azurajs/decorators";
import { ApiDoc } from "azurajs/swagger";

@Controller("/users")
class UserController {
  @Get("/:id")
  @ApiDoc({
    summary: "Get user by ID",
    description: "Retrieve detailed information about a specific user"
  })
  async getUser(@Req() req, @Res() res) {
    res.json({ id: req.params.id, name: "John Doe" });
  }
}

@ApiResponse()

Define as respostas possíveis do endpoint:

@Get("/:id")
@ApiResponse(200, "User found", {
  example: { 
    id: 1, 
    name: "John Doe", 
    email: "[email protected]" 
  }
})
@ApiResponse(404, "User not found", {
  example: { error: "User not found" }
})
async getUser(@Req() req, @Res() res) {
  // ...
}

@ApiParameter()

Documenta parâmetros da requisição:

@Get("/:id")
@ApiParameter("id", "path", {
  description: "User ID",
  required: true,
  schema: { type: "integer" },
  example: 123
})
async getUser(@Req() req, @Res() res) {
  // ...
}

Tipos de parâmetros:

  • path - Parâmetros na URL (/users/:id)
  • query - Query strings (/users?page=1)
  • header - Headers HTTP
  • cookie - Cookies

@ApiBody()

Documenta o corpo da requisição:

@Post("/")
@ApiBody({
  description: "User data",
  required: true,
  example: {
    name: "Jane Doe",
    email: "[email protected]",
    age: 25
  }
})
async createUser(@Req() req, @Res() res) {
  res.status(201).json(req.body);
}

@ApiTags()

Organiza endpoints por tags:

@Controller("/users")
@ApiTags("Users")
class UserController {
  // Todos os endpoints terão a tag "Users"
}

// Ou em endpoints específicos:
@Get("/")
@ApiTags("Users", "Public")
async listUsers() {
  // ...
}

@ApiSecurity()

Especifica requisitos de autenticação:

@Get("/profile")
@ApiSecurity("bearerAuth")
async getProfile(@Req() req, @Res() res) {
  // Endpoint protegido
}

Configurando security schemes:

setupSwaggerWithControllers(app, {
  title: "My API",
  version: "1.0.0",
  securitySchemes: {
    bearerAuth: {
      type: "http",
      scheme: "bearer",
      bearerFormat: "JWT"
    },
    apiKey: {
      type: "apiKey",
      in: "header",
      name: "X-API-Key"
    }
  }
}, [UserController]);

@ApiDeprecated()

Marca endpoints como deprecados:

@Get("/old-endpoint")
@ApiDeprecated()
@ApiDoc({ summary: "Old endpoint (use /new-endpoint instead)" })
async oldEndpoint(@Req() req, @Res() res) {
  // ...
}

JavaScript Simples (Sem Decorators)

Se você prefere JavaScript puro ou não quer usar decorators, use o método addDoc():

import { AzuraClient } from "azurajs";
import { setupSwagger, SwaggerGenerator } from "azurajs/swagger";

const app = new AzuraClient();

// Criar gerador Swagger
const swagger = new SwaggerGenerator({
  title: "API Simples",
  description: "API documentada de forma simples",
  version: "1.0.0",
  tags: [
    { name: "Users", description: "Gerenciamento de usuários" },
    { name: "Binary", description: "Operações de codificação" }
  ]
});

// Rota GET com query parameter
app.get("/binary/encode", (req, res) => {
  const text = req.query.q;
  if (!text) {
    return res.status(400).json({ error: "Query 'q' is required" });
  }

  const binary = text.split("").map(c => 
    c.charCodeAt(0).toString(2).padStart(8, "0")
  ).join("");

  res.json({
    input: text,
    output: binary
  });
});

// Documentar com addDoc() - FÁCIL! ⭐
swagger.addDoc({
  method: "GET",
  path: "/binary/encode",
  summary: "Codifica texto em binário",
  description: "Converte qualquer texto para representação binária",
  tags: ["Binary"],
  parameters: [
    {
      name: "q",
      in: "query",
      description: "Texto a ser codificado",
      required: true,
      schema: { type: "string" },
      example: "Hello"
    }
  ],
  responses: {
    200: {
      description: "Texto codificado com sucesso",
      example: {
        input: "Hello",
        output: "0100100001100101011011000110110001101111"
      }
    },
    400: {
      description: "Parâmetro ausente",
      example: { error: "Query 'q' is required" }
    }
  }
});

// Rota POST com body
app.post("/users", (req, res) => {
  const newUser = { id: Date.now(), ...req.body };
  res.status(201).json(newUser);
});

swagger.addDoc({
  method: "POST",
  path: "/users",
  summary: "Cria novo usuário",
  tags: ["Users"],
  requestBody: {
    description: "Dados do usuário",
    required: true,
    example: {
      name: "João Silva",
      email: "[email protected]"
    }
  },
  responses: {
    201: {
      description: "Usuário criado",
      example: {
        id: 1234567890,
        name: "João Silva",
        email: "[email protected]"
      }
    }
  }
});

// Rota GET com path parameter
app.get("/users/:id", (req, res) => {
  res.json({
    id: req.params.id,
    name: "John Doe",
    email: "[email protected]"
  });
});

swagger.addDoc({
  method: "GET",
  path: "/users/:id",
  summary: "Busca usuário por ID",
  tags: ["Users"],
  parameters: [
    {
      name: "id",
      in: "path",
      description: "ID do usuário",
      required: true,
      schema: { type: "string" },
      example: "123"
    }
  ],
  responses: {
    200: {
      description: "Usuário encontrado",
      example: {
        id: "123",
        name: "John Doe",
        email: "[email protected]"
      }
    },
    404: {
      description: "Usuário não encontrado"
    }
  }
});

// Setup Swagger UI
setupSwagger(app, swagger);

app.listen(3000).then(() => {
  console.log("🚀 Server: http://localhost:3000");
  console.log("📚 Docs: http://localhost:3000/docs");
});

Vantagens do addDoc():

  • JavaScript puro - Funciona sem TypeScript ou decorators
  • Simples - Menos verboso que SwaggerHelpers.createResponse
  • Automático - Gera responses OpenAPI a partir de exemplos
  • Flexível - Misture com rotas tradicionais do AzuraJS

Exemplo Completo (TypeScript com Decorators)

import { Controller, Get, Post, Put, Delete, Req, Res } from "azurajs/decorators";
import { Swagger } from "azurajs/swagger";

@Controller("/users")
class UserController {
  @Get("/")
  @Swagger({
    summary: "Lista todos os usuários",
    tags: ["Users"],
    parameters: [
      {
        name: "page",
        in: "query",
        schema: { type: "number" },
        example: 1
      },
      {
        name: "limit",
        in: "query",
        schema: { type: "number" },
        example: 10
      }
    ],
    responses: {
      200: {
        description: "Lista de usuários",
        example: [
          { id: 1, name: "John Doe" },
          { id: 2, name: "Jane Smith" }
        ]
      }
    }
  })
  async listUsers(@Req() req, @Res() res) {
    res.json([
      { id: 1, name: "John Doe" },
      { id: 2, name: "Jane Smith" }
    ]);
  }

  @Get("/:id")
  @Swagger({
    summary: "Busca usuário por ID",
    tags: ["Users"],
    parameters: [
      {
        name: "id",
        in: "path",
        required: true,
        schema: { type: "string" },
        example: "123"
      }
    ],
    responses: {
      200: {
        description: "Usuário encontrado",
        example: { id: "123", name: "John Doe", email: "[email protected]" }
      },
      404: {
        description: "Usuário não encontrado"
      }
    }
  })
  async getUser(@Req() req, @Res() res) {
    res.json({ id: req.params.id, name: "John Doe" });
  }

  @Post("/")
  @Swagger({
    summary: "Cria novo usuário",
    tags: ["Users"],
    requestBody: {
      description: "Dados do usuário",
      required: true,
      content: {
        "application/json": {
          schema: {
            type: "object",
            properties: {
              name: { type: "string" },
              email: { type: "string", format: "email" }
            },
            required: ["name", "email"]
          },
          example: {
            name: "Jane Doe",
            email: "[email protected]"
          }
        }
      }
    },
    responses: {
      201: {
        description: "Usuário criado",
        example: { id: 123, name: "Jane Doe", email: "[email protected]" }
      }
    }
  })
  async createUser(@Req() req, @Res() res) {
    res.status(201).json(req.body);
  }

  @Delete("/:id")
  @Swagger({
    summary: "Deleta usuário",
    tags: ["Users"],
    parameters: [
      {
        name: "id",
        in: "path",
        required: true,
        schema: { type: "string" },
        example: "123"
      }
    ],
    responses: {
      204: {
        description: "Usuário deletado"
      },
      404: {
        description: "Usuário não encontrado"
      }
    }
  })
  async deleteUser(@Req() req, @Res() res) {
    res.status(204).send();
  }
}

Decorators Individuais - Exemplo Completo

Se preferir usar os decorators tradicionais separadamente:

import { 
  ApiDoc, 
  ApiResponse, 
  ApiParameter, 
  ApiBody, 
  ApiTags 
} from "azurajs/swagger";

@Controller("/users")
@ApiTags("Users")
class UserController {
  @Get("/")
  @ApiDoc({
    summary: "List all users",
    description: "Retrieve a paginated list of users"
  })
  @ApiParameter("page", "query", {
    description: "Page number",
    schema: { type: "integer", default: 1 }
  })
  @ApiParameter("limit", "query", {
    description: "Items per page",
    schema: { type: "integer", default: 10 }
  })
  @ApiResponse(200, "Success", {
    example: {
      users: [
        { id: 1, name: "John Doe" },
        { id: 2, name: "Jane Doe" }
      ],
      total: 2,
      page: 1
    }
  })
  async listUsers(@Req() req, @Res() res) {
    const page = req.query.page || 1;
    const limit = req.query.limit || 10;
    res.json({ users: [], total: 0, page });
  }

  @Get("/:id")
  @ApiDoc({
    summary: "Get user by ID",
    description: "Retrieve detailed user information"
  })
  @ApiParameter("id", "path", {
    description: "User ID",
    required: true,
    schema: { type: "integer" }
  })
  @ApiResponse(200, "User found", {
    example: { id: 1, name: "John Doe", email: "[email protected]" }
  })
  @ApiResponse(404, "User not found")
  async getUser(@Req() req, @Res() res) {
    const userId = req.params.id;
    res.json({ id: userId, name: "John Doe" });
  }

  @Post("/")
  @ApiDoc({
    summary: "Create new user",
    description: "Create a new user in the system"
  })
  @ApiBody({
    description: "User data",
    required: true,
    example: {
      name: "Jane Doe",
      email: "[email protected]",
      age: 25
    }
  })
  @ApiResponse(201, "User created", {
    example: { id: 3, name: "Jane Doe", email: "[email protected]" }
  })
  @ApiResponse(400, "Invalid data")
  async createUser(@Req() req, @Res() res) {
    res.status(201).json({ id: 3, ...req.body });
  }

  @Put("/:id")
  @ApiDoc({ summary: "Update user" })
  @ApiParameter("id", "path", {
    description: "User ID",
    required: true,
    schema: { type: "integer" }
  })
  @ApiBody({
    description: "Updated user data",
    example: { name: "John Updated" }
  })
  @ApiResponse(200, "User updated")
  @ApiResponse(404, "User not found")
  async updateUser(@Req() req, @Res() res) {
    res.json({ id: req.params.id, ...req.body });
  }

  @Delete("/:id")
  @ApiDoc({ summary: "Delete user" })
  @ApiParameter("id", "path", {
    description: "User ID",
    required: true
  })
  @ApiResponse(204, "User deleted")
  @ApiResponse(404, "User not found")
  async deleteUser(@Req() req, @Res() res) {
    res.status(204).send();
  }
}

Interface Swagger UI

A interface Swagger do AzuraJS oferece:

🎨 Design Moderno

  • Dark mode premium (inspirado em Stripe/Vercel)
  • Sidebar com navegação por tags
  • Cards expansíveis para cada endpoint
  • Syntax highlighting para código

🔍 Busca em Tempo Real

  • Pesquise endpoints por método ou path
  • Filtragem instantânea na sidebar

🚀 Try It Out

  • Teste endpoints diretamente na UI
  • Preencha parâmetros, query strings e body
  • Veja a resposta em tempo real
  • Exibe tempo de resposta

📋 Funcionalidades

  • Copiar código com um clique
  • Visualização de status codes
  • Exemplos de request/response
  • Documentação de tipos
  • Badges para required/deprecated

Configuração Avançada

Múltiplos Servidores

setupSwaggerWithControllers(app, {
  title: "My API",
  version: "1.0.0",
  servers: [
    {
      url: "http://localhost:3000",
      description: "Development"
    },
    {
      url: "https://api.example.com",
      description: "Production"
    },
    {
      url: "https://staging.example.com",
      description: "Staging"
    }
  ]
}, [UserController]);

Security Schemes

setupSwaggerWithControllers(app, {
  title: "My API",
  version: "1.0.0",
  securitySchemes: {
    bearerAuth: {
      type: "http",
      scheme: "bearer",
      bearerFormat: "JWT",
      description: "JWT token from /auth/login"
    },
    apiKey: {
      type: "apiKey",
      in: "header",
      name: "X-API-Key",
      description: "API key from dashboard"
    },
    oauth2: {
      type: "oauth2",
      flows: {
        authorizationCode: {
          authorizationUrl: "https://example.com/oauth/authorize",
          tokenUrl: "https://example.com/oauth/token",
          scopes: {
            "read:users": "Read user data",
            "write:users": "Modify user data"
          }
        }
      }
    }
  }
}, [UserController]);

Contact & License

setupSwaggerWithControllers(app, {
  title: "My API",
  description: "Complete API documentation",
  version: "1.0.0",
  contact: {
    name: "API Support",
    email: "[email protected]",
    url: "https://example.com/support"
  },
  license: {
    name: "MIT",
    url: "https://opensource.org/licenses/MIT"
  }
}, [UserController]);

Setup Manual (Avançado)

Se você precisa de mais controle, pode configurar manualmente:

import { AzuraClient } from "azurajs";
import { SwaggerIntegration, SwaggerGenerator } from "azurajs/swagger";
import { getControllerMetadata } from "azurajs/decorators";

const app = new AzuraClient();

// Criar gerador
const generator = new SwaggerGenerator({
  title: "My API",
  version: "1.0.0"
});

// Adicionar controllers
const metadata = getControllerMetadata(UserController);
generator.addController(metadata.prefix, metadata.routes);

// Configurar rotas Swagger
const swagger = new SwaggerIntegration(generator, {
  title: "My API",
  version: "1.0.0"
});

app.use("/docs", swagger.getUIRoute());
app.use("/api-spec.json", swagger.getSpecRoute());

// Registrar controllers normalmente
app.registerController(UserController);

app.listen();

TypeScript Types

O AzuraJS fornece types completos para OpenAPI 3.0:

import type {
  OpenAPIDocument,
  SwaggerConfig,
  ApiDocMetadata,
  ApiResponseMetadata,
  ParameterObject,
  SchemaObject
} from "azurajs/swagger";

const config: SwaggerConfig = {
  title: "My API",
  version: "1.0.0",
  servers: [{ url: "http://localhost:3000" }]
};

Boas Práticas

1. Use @ApiTags para Organização

Agrupe endpoints relacionados com tags:

@Controller("/users")
@ApiTags("Users", "Management")
class UserController { }

@Controller("/products")
@ApiTags("Products", "E-commerce")
class ProductController { }

2. Forneça Exemplos Detalhados

Exemplos ajudam desenvolvedores a entender o formato esperado:

@ApiBody({
  description: "User registration data",
  example: {
    name: "John Doe",
    email: "[email protected]",
    password: "secure123",
    preferences: {
      newsletter: true,
      notifications: ["email", "push"]
    }
  }
})

3. Documente Todos os Status Codes

Inclua todas as respostas possíveis:

@ApiResponse(200, "Success")
@ApiResponse(400, "Bad request")
@ApiResponse(401, "Unauthorized")
@ApiResponse(403, "Forbidden")
@ApiResponse(404, "Not found")
@ApiResponse(500, "Server error")

4. Use Descriptions Claras

Seja descritivo nas summaries e descriptions:

@ApiDoc({
  summary: "Update user profile",
  description: "Updates the authenticated user's profile information. Only provided fields will be updated. Requires valid JWT token."
})

5. Marque Endpoints Deprecados

Informe quando endpoints estão deprecados:

@Get("/old-api")
@ApiDeprecated()
@ApiDoc({
  summary: "Legacy API endpoint",
  description: "⚠️ Deprecated: Use /api/v2/endpoint instead. This endpoint will be removed in v3.0.0"
})

Troubleshooting

Documentação não aparece

Certifique-se de que:

  1. Você importou os decorators de azurajs/swagger
  2. Os decorators estão aplicados nos métodos
  3. Os controllers foram passados para setupSwaggerWithControllers()

Try It Out não funciona

Verifique:

  1. CORS está configurado corretamente
  2. O servidor está rodando na URL esperada
  3. Os parâmetros estão sendo preenchidos corretamente

Tipos TypeScript não funcionam

Certifique-se de importar os types:

import type { SwaggerConfig } from "azurajs/swagger";

Próximos Passos

On this page