Table of Contents
Em se tratando de aplicações back-end, é essencial pensarmos em expor uma documentação, pois ela será fundamental para um melhor entendimento de como os endpoints funcionam e o que podem ou não fazer.
Não iremos focar em detalhes de implementação, por isso vamos partir do pressuposto que sua aplicação está minimamente configurada e possui pelo menos uma rota de API exposta.
Especificação
O primeiro ponto que precisamos levar em consideração é sobre a definição de regras e comportamentos de determinado endpoint, isso diz respeito à:
- Verbo HTTP suportado;
- Parâmetros de entrada;
- Modelos de resposta de sucesso e/ou erro;
- Modo de autenticação;
- Permissões quando disponível.
Bom, você pode estar se perguntando: "Ok, precisamos levantar várias definições para uma API, como faremos isso na prática?" (Se não se perguntou isso ainda, essa é a hora...), a resposta é: Open API!
O que é OpenAPI?
OpenAPI Specification (OAS) é um conjunto de conceitos e padrões para definir a estrutura e comportamento de APIs no protocolo HTTP, a ideia central é termos uma maneira agnóstica para essas regras que geralmente são definidas em um arquivo YAML ou JSON.
Principais pontos a se destacar ao utilizar as especificações da OAS:
- Facilidade de identificação de serviços e contratos das APIs por meio da padronização.
- Possibilidade de gerar código e testes com base nas definições do arquivo.
- Automação de infraestrutura.
Utilizando em nosso código
Cada linguagem e framework terá sua maneira de implementar as especificações da OpenAPI, mas no geral todas irão seguir a mesma linha, ao criar um arquivo com as regras de todos os recursos expostos pela API.
Eis aqui um exemplo:
openapi: 3.0.0
info:
title: Sample API
description: A sample API to demonstrate OpenAPI specification
version: 1.0.0
servers:
- url: https://api.example.com/v1
description: Main production server
tags:
- name: Users
description: Operations related to users
- name: Products
description: Operations related to products
paths:
/users:
get:
summary: Get all users
tags:
- Users
responses:
'200':
description: A list of users
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/User'
'500':
description: Internal server error
components:
schemas:
User:
type: object
properties:
id:
type: string
example: 12345
name:
type: string
example: John Doe
email:
type: string
example: john.doe@example.com
createdAt:
type: string
format: date-time
example: 2023-10-01T12:00:00Z
No exemplo acima, existe apenas um endpoint disponível: Get /users
, você poderia passar horas escrevendo cada um, mas existem maneiras mais rápidas e eficientes de se fazer isso.
Automatizando o processo
Existem muitas maneiras de automatizar o processo de geração do arquivo com as especificações, vamos usar aqui uma estratégia que particularmente considero eficiente e ao mesmo tempo nos dá controle e transparência no código.
Usaremos como exemplo uma aplicação em NodeJS
e duas bibliotecas para lidar com o processo de automação, sendo elas:
O processo de configuração é bem simples, primeiro precisamos criar um arquivo na raiz do projeto, dei o nome de: swagger.ts
:
// swagger.ts
import swaggerJSDoc from 'swagger-jsdoc'
const swaggerDefinition = {
openapi: '3.0.0',
info: {
title: 'Gen AI API',
version: '1.0.0',
description:
'This is an Express service that provides authorization functionality and includes gen-AI features using RAG, Redis, Postgres, and Langchain.',
},
servers: [
{
url: 'http:localhost:3000',
},
],
tags: [
{
name: 'Auth',
description: 'Endpoints related to authentication',
},
{
name: 'Users',
description: 'Endpoints related to users management',
},
{
name: 'AI',
description: 'Endpoints related to gen-AI features',
},
],
components: {
securitySchemes: {
bearerAuth: {
type: 'http',
scheme: 'bearer',
bearerFormat: 'JWT',
},
},
},
}
const options = {
swaggerDefinition,
apis: ['./src/modules/**/application/routes/*.ts'],
}
const swaggerSpec = swaggerJSDoc(options)
export default swaggerSpec
No código acima definimos alguns metadados para a descrição da API no geral, vale notar o campo: tags
, esse array de objetos permite adicionar seções para agrupar os endpoints por tipos.
Caso queira entender melhor os valores permitidos para o arquivo de especificação, visite a documentação oficial.
Agora em cada rota da aplicação iremos adicionar um comentário com a especificação dessa rota em específico. Isso é importante, pois a aplicação irá fazer a leitura com base nos arquivos de rotas informado no campo apis
demonstrado no arquivo swagger.ts
no exemplo anterior.
// route.ts
import express from 'express'
const router = express.Router()
/**
* @swagger
* /status:
* get:
* summary: Returns a message to validade if API server is running
* responses:
* 200:
* description: A successful response
* content:
* application/json:
* schema:
* type: object
* properties:
* message:
* type: string
* example: Systems up and running!
*/
router.get('/', (_, res) => {
res.status(200).send({ message: 'Systems up and running!' })
})
export default router
Ao adicionar a anotação @swagger
no início do comentário, estamos indicando que esse bloco deverá ser utilizado para a inclusão dessa rota na documentação, que ao final será mesclado com o restante do conteúdo.
Agora iremos expor um endpoint para visualizar a documentação, para isso, vá até o arquivo app.ts
(ou correlato) da sua aplicação e adicione:
import express from 'express'
import swaggerUi from 'swagger-ui-express'
const app = express()
const port = process.env['PORT'] || config.port
...
app.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec))
...
app.listen(port, () => {
console.log(`Server listening to http://localhost:${port}`)
})
Simples assim, ao executar a aplicação a documentação estará disponível no endpoint /docs
.
Extra: Automatizando a automação usando agentes de IA
Até aqui ok, mas temos outro ponto, ainda precisamos de um pouco mais de esforço manual para a escrita dos comentários de cada endpoint. Graças aos modelos de AI generativa existente, podemos simplificar ainda mais esse processo, e solicitar que a AI gere o comentário no formato que precisamos.
Confira abaixo o prompt:
Generate the JSDocs comments using OpenAPI for the following API specs:
"""
endpoint name: /users/{id}
path parameter type: string
method: PUT
request body: {
"name": "John Doe",
"email": "john.doe@email.com",
}
summary: Update a user by given id and return it
200_response: {
"success": true,
"data": [
{
"id": "367b2539-bef4-412b-b94d-c9d2178dcdaa",
"name": "John Doe",
"email": "john.doe@gmail.com",
"createdAt": "2024-09-30T21:04:18.656Z"
}
]
}
400_response: {
"success": false,
"error": {
"message": [
"Invalid email address",
"name field is required"
]
}
}
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer <token>"
}
"""
More context: I'm using `swagger-ui-express` and `swagger-jsdoc` libraries to setup the Swagger documentation.
Give me only the response, without explanations.
Aqui estamos adicionando todas as informações necessárias para esse endpoint e também os exemplos de resposta, além de informar quais bibliotecas estaremos utilizando para a geração da documentação, essas informações adicionais são importantes porque dão mais contexto para o modelo e com isso recebemos respostas mais precisas.
Confira abaixo a resposta do agente para esse prompt:
/**
* @swagger
* /users/{id}:
* put:
* summary: Update a user by given id and return it
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* name:
* type: string
* example: John Doe
* email:
* type: string
* example: john.doe@email.com
* responses:
* 200:
* description: A successful response
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* data:
* type: object
* properties:
* id:
* type: string
* example: 367b2539-bef4-412b-b94d-c9d2178dcdaa
* name:
* type: string
* example: John Doe
* email:
* type: string
* example: john.doe@gmail.com
* createdAt:
* type: string
* format: date-time
* example: 2024-09-30T21:04:18.656Z
* 400:
* description: Invalid request
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: false
* error:
* type: object
* properties:
* message:
* type: array
* items:
* type: string
* example: ["Invalid email address", "name field is required"]
* security:
* - bearerAuth: []
*/
Agora só precisamos conferir e fazer os ajustes se necessários no comentário gerado e adicionar logo acima da definição do endpoint da API.
Finalizando
Neste artigo aprendemos um pouco mais sobre o padrão OpenAPI e como podemos automatizar o processo de documentar uma API, além de fazer o uso de agentes de AI para facilitar ainda mais o processo.
Espero que esse conteúdo tenha trazido algum conhecimento novo pra você ou até mesmo insights para novas ideias, obrigado pelo seu tempo de leitura e te vejo na próxima! :)
Confira abaixo como ficou a geração da documentação do meu projeto, e caso queira entender melhor e conferir a implementação -- É só seguir esse link.