| Data de elaboração | 18/08/2021 |
| Responsável pelo estudo | Diego Gonçalves de Almeida (Assessor) |
| Equipe do estudo | Esquadrão Suicida |
| Alvo | Swagger |
| Origem | Objetivo estratégico: Padronização de documentação de APIs |
| Objetivo | Definição da melhor abordagem para padronização de documentações de APIs node.js usando o Swagger no âmbito da Superintendência de Tecnologia da Informação e Comunicação - SETIC. Atualmente, as API’s são documentadas usando o Postman que, como tem integração com a rout, basta apenas algumas marcações no código-fonte para que a documentação seja gerada. Todavia, com a mudança do Postman para o Swagger, aplicação adotada como padrão no âmbito da SETIC, visando a implementação de dicionário de dados, exemplos de resposta variados abordando diversos casos e também a documentação de erros possíveis, faz-se necessário definir padrões de documentação que não polua o código excessivamente e de fácil manutenção/geração. |
| Documentação correlata (opcional) | https://www.npmjs.com/package/swagger-ui-express - https://www.npmjs.com/package/swagger-jsdoc - https://dev.to/kabartolo/how-to-document-an-express-api-with-swagger-ui-and-jsdoc-50do - https://www.npmjs.com/package/express-jsdoc-swagger - https://github.com/epiphone/routing-controllers-openapi |
| Observações | Não possui. |
Glossário
Maior produtividade na confecção da documentação com o uso de funções já existentes no Typescript, sem prejuízo da manutenção da aplicação, pois o código permanece ‘limpo’. Com base nos estudos realizados decidimos utilizar o swagger, diversos times hoje usam esta ferramenta para documentação, logo, tonaria mais fácil a utilização para os mesmos.
Nos testes realizados constatamos que esta biblioteca serve como pilar para as demais que tramalham com Swagger, a configuração é simples porem a escrita de documentação exige muita escrita tornando a manutenção árdua e suscetível a erro, oferece a opção de documentação em forma de comentários no código mas que se prova inviável pois polui visualmente o documento tornando difícil a compreensão e manutenção do mesmo.
Exemplo de implementação Swagger UI Express
yarn add swagger-ui-express
const express = require(‘express’);
const app = express();
const swaggerUi = require(‘swagger-ui-express’);
const swaggerDocument = require(‘./swagger.json’);var options = {
swaggerOptions: {
validatorUrl: null
}
};
app.use(‘/api-docs’, swaggerUi.serve, swaggerUi.setup(swaggerDocument, options));
/**
- @swagger
- /:
get:summary: Lista de produtosdescription: Lista de produtos usada para popular a grid e produtosresponses:200:description: listacontent:application/json:schema:type: arrayitems:type: arrayitems:type: objectproperties:id:type: stringdescription: id do produtoexample: '0002321'categoria_id:type: stringdescription: id da categoriaexample: '0254'ncm_id:type: stringdescription: id do ncmexample: '329'nome:type: stringdescription: nome do produtoexample: Produto testeinativo?:type: numberdescription: flag que indica se o produto esta ativo (1 ou null)example: 1*/
route.get(‘/’, (req, res) => {
…
})
Biblioteca que utiliza a ‘swagger UI Express’ citada anteriormente, segue a mesma premissa de documentação através de escrita de comentários no código e apresenta os mesmos problemas, poluição visual e dificuldade de manutenção.
Biblioteca que também utiliza a ‘swagger UI Express’, porem trás uma nova abordagem utilizando funções existentes no Typescript, usando decoradores podemos definir tipos de retornos, retornos múltiplos, casos onde ocorrem erros e exemplos de resposta, com o auxilio de outra biblioteca, ‘class-validator-jsonschema’, podemos definir schema de dados que nos ajuda a não repetir código e também oferecer um dicionário de dados.
Segue a baixo um exemplo de implementação com a routing-controllers-openapi em um ambiente de teste.
yarn add routing-controllers-openapi
Dependências
yarn add class-validator-jsonschema # para criar dicionários de dados
yarn add swagger-ui-express # para servir a documentação no swagger
// importação das bibliotecas
import swagger from «swagger-ui-express»;
import { routingControllersToSpec } from «routing-controllers-openapi»;
import { validationMetadatasToSchemas } from «class-validator-jsonschema»;// configuração do class-validator-jsonschema
const schemas = validationMetadatasToSchemas({
refPointerPrefix: «#/components/schemas/»,
});// recupera metadata das rotas
const storage = getMetadataArgsStorage();// gera as especificações da documentação no padrão OpenAPI
const spec = routingControllersToSpec(
storage,
{},
{
components: { schemas },
},
);app.use(«/doc», swagger.serve, swagger.setup(spec)); // cria uma rota para servir a documentação
import { IsNumber, IsString } from «class-validator»;
import { JSONSchema } from «class-validator-jsonschema»;const example1 = {
id: «001»,
nome: «Example»,
idade: 32,
};const example2 = {
id: «001»,
nome: «Example»,
idade: null,
};@JSONSchema({ examples: [example1, example2] })
export class User {
@IsString()
id!: string;@IsString()
nome!: string;@IsNumber()
idade?: number;
}
import { Body, Delete, Get, HttpCode, JsonController, Param, Post, Put } from «routing-controllers»;
import { OpenAPI, ResponseSchema } from «routing-controllers-openapi»;
import { ApplicationError } from «…/error/ApplicationError»;
import { UserService } from ‘…/services/user.service’;
import { User } from «…/models/User.schema»;@JsonController(«/users»)
export class UserController {
@Get(«/»)
@ResponseSchema(User, { isArray: true })
@ResponseSchema(ApplicationError, { statusCode: 400 })
async list() {
return await UserService.list()
}@Get(«/:userId»)
@ResponseSchema(User)
@ResponseSchema(ApplicationError, { statusCode: 400 })
async show(@Param(«userId») userId: string): Promise {
return await UserService.getUserById(userId)
}@HttpCode(201)
@Post(«/»)
@ResponseSchema(User)
async store(@Body() user: User) {return await UserService.createUser(user);}
@Put(«/:userId»)
@ResponseSchema(User)
async edit(@Param(«userId») userId: string, @Body() user: User) {
return user;
}@Delete(«/:userId»)
@HttpCode(204)
@ResponseSchema(«», { statusCode: 204 })
@ResponseSchema(ApplicationError, { statusCode: 404 })
async delete(@Param(«userId») userId: string) {
return await UserService.deleteUserById(userId)
}
}
Dessa forma a utilização da ‘routing-controller-openapi’ se mostrou mais eficaz dado que:
Desta forma, usando a melhor abordagem, sera possível documentar qualquer endpoint usando swagger com 2 pontos de complexidade anti o 1 ponto usado no postman.
[1] SWAGGER UI EXPRESS. Disponível em: https://www.npmjs.com/package/swagger-ui-express. Acesso em: 18 ago. 2021.
[2] SWAGGER JSDOC. Disponível em: https://www.npmjs.com/package/swagger-jsdoc. Acesso em: 18 ago. 2021.
[3] How to Document an Express API with Swagger UI and JSDoc. 2020. Disponível em: https://dev.to/kabartolo/how-to-document-an-express-api-with-swagger-ui-and-jsdoc-50do. Acesso em: 18 ago. 2021.
[4] EXPRESS JSDOC SWAGGER. Disponível em: https://www.npmjs.com/package/express-jsdoc-swagger. Acesso em: 18 ago. 2021.
https://documentos.sistemas.ro.gov.br/link/1283#bkmrk-[5]-routing-controll
[5] ROUTING CONTROLLER OPENAPI. Disponível em: https://github.com/epiphone/routing-controllers-openapi. Acesso em: 18 ago. 2021.