OpenAPI Requests
Uma requisição para um path está associada à ação que irá executar que será um Operation Object. Só para recordar são os métodos que temos no OpenAPI, mas até agora só usamos o Get, porém para um mesmo path temos vários métodos possíveis.
path:
/products: # Path Object
# Todos esses são path items objects
$ref: "" # Espera uma string
summary: ""
description: ""
get: {} # Espera um Operation Object aqui dentro #<<<<<<
put: {} # Espera um Operation Object aqui dentro
post: {} # Espera um Operation Object aqui dentro
delete: {} # Espera um Operation Object aqui dentro
options: {} # Espera um Operation Object aqui dentro
head: {} # Espera um Operation Object aqui dentro
patch: {} # Espera um Operation Object aqui dentro
trace: {} # Espera um Operation Object aqui dentro
servers: {} # Espera um server object aqui dentro
parameters: {} # Espera um Parameter Reference aqui dentro
No método get que irá definir um operation object só utilizamos o parameters e o responses até agora.
path:
#...
/v1/customers/{Id}:
get: # Operation Object para o path acima.
parameters: # <<<
- $ref: '#/components/parameters/Id'
responses: # <<<
'200':
description: Found Customer
content:
application/json:
schema:
$ref: '#/components/schemas/Customer'
Para um mesmo path podemos definir um operation object para cada método, porém não é possível definir dois gets diferentes. Um get pode gerar vários responses.
Summary e Description
Sempre precisamos pensar em quem irá utilizar a nossa API, então é boa prática esclarecer o máximo possível cada coisa.
Em todos os operations objects temos summary
e description
que valem a pena especificar para serem mostrados na API.
Sabendo que o summary tem um espaço curto seja direto e seja mais detalhista no description.
Tags
Não é possível criar tags globalmente para todos os métodos dentro de um path. É necessário criar as tags dentro de cada dos métodos. Podemos ter uma lista de tags.
Quando não definimos uma tag todos ficam em default.
Uma vez que um nome de tag aparece um agrupamento visual será feito. Se uma operação participar de duas tags aparecerá em dois grupos mesmo que seja necessário repetir. Dessa forma fica mais flexível do que declarar tags globalmente.
openapi: 3.0.2
info:
# ...
paths:
/v1/customers:
get:
summary: List of Customers
description: Get a list of customers in the system
tags:
- Customer
- V1
# ...
/v1/customers/{Id}:
get:
summary: Get a Customer
description: Get a single customer by its ID value
tags:
- Customer
- V1
# ...
/v1/beers:
get:
summary: List of beers
description: Get a list of beers in the system
tags:
- Beer
- V1
# ...
/v1/beer/{Id}:
get:
summary: Get a Customer
description: Get a single customer by its ID value
tags:
- Beer
- V1
# ...
OperationId (opcional)
No OpenAPI, o operationId é um identificador único para uma operação. É usado para diferenciar cada operação em um endpoint e, muitas vezes, é utilizado por ferramentas geradoras de código para criar funções ou métodos correspondentes àquela operação. Ele serve principalmente como uma ponte entre a especificação OpenAPI e o código gerado ou ferramentas que consomem a API, como SDKs ou sistemas de integração.
- Geração de Código: Ferramentas como Swagger Codegen ou OpenAPI Generator utilizam o operationId para nomear funções/métodos nos SDKs gerados.
- Se operationId for getUserById, o código gerado pode conter um método como getUserById().
- Facilidade de Navegação: Permite que APIs grandes tenham uma referência única para cada operação, facilitando o mapeamento entre a especificação e o código.
- Garantir que cada operação seja única evita conflitos em implementações e documentações.
- A nomemclatura deve refletir a ação que a operação realiza.
- Siga um padrão para nomear operationId, como verboRecurso (ex.: createUser, deleteOrder).
Embora o operationId seja opcional, adicioná-lo é altamente recomendado, especialmente para projetos que utilizam ferramentas de documentação ou geração de código.
Se não está utilizando ferramentas que dependem diretamente do operationId, não há impacto técnico imediato no funcionamento da API. A API continuará funcionando normalmente, desde que você implemente corretamente os endpoints e o roteamento no backend.
-
Se o operationId na especificação não corresponde à forma como o endpoint é tratado no código, pode causar confusão para outros desenvolvedores ou equipes que consultam a documentação. Alguém olhando a especificação pode esperar que exista um método chamado getUserById, mas no código você tratou como fetchUser.
-
Se no futuro você decidir usar geração de código ou criar SDKs baseados na API, a inconsistência entre o operationId e o código pode gerar métodos mal nomeados ou causar necessidade de retrabalho.
-
Algumas ferramentas de documentação ou teste podem usar o operationId para identificar operações unicamente. Se ele não corresponde à lógica do código, pode dificultar a automação.
Vamos definir nossos operationsID para depois gerar um código com essa API.
openapi: 3.0.2
info:
# ...
paths:
/v1/customers:
get:
summary: List of Customers
description: Get a list of customers in the system
operationId: listCustomersV1 #<<<<
tags:
- Customer
# ...
/v1/customers/{Id}:
get:
summary: Get a Customer
description: Get a single customer by its ID value
operationId: getCustomerById #<<<<
tags:
- Customer
# ...
/v1/beers:
get:
summary: List of beers
description: Get a list of beers in the system
operationId: listBeers #<<<<
tags:
- Beer
# ...
/v1/beer/{Id}:
get:
summary: Get a Customer
description: Get a single customer by its ID value
operationId: getBeerById #<<<<
tags:
- Beer
# ...
# ...
Request Body
Tecnicamente o que temos disponível em cada operation objetct é isso.
- tags
- description
- externalDocs (Também pode ser usado em nível da api)
# Em nível da api
externalDocs:
description: Learn more about this API
url: https://example.com/docs/api
path:
/v1/user
get:
# Nível de método
ExternalDocs:
description: Learn more about this endpoint
url: https://example.com/docs/api/v1/user
- operationId
- parameters
- requestBody (Nosso próximo aprendizado)
- responses
- callbacks
- deprecate
- security
- servers
O método HTTP GET pode ter um body mas essa prática é altamente desencorajada e incomum assim como HEAD e OPTIONS. Revise a tabela de métodos e veja que esses métodos servem apenas para recuperar dados e não para alterar dados.
Muitas implementações de servidores, proxies e bibliotecas HTTP ignoram ou rejeitam um body em requisições GET.
Mas vamos fazer uma validação fora desse plugin do vscode para testar.
# Vamos usar a ferramenta openapi-generator só para testar rápido por enquanto o que falei acima.
openapi-generator validate -i infoapi.yaml
Validating spec (infoapi.yaml)
No validation issues detected. # Passa mas não é para usar
Criando um POST
Agora vamos montar um método POST que irá usar um requestBody.
paths:
/v1/customers:
get:
#....
post:
summary: New Customer
description: Create a new customer
operationId: newCustomer
tags:
- Customer
requestBody: # O Body que iremos precisar.
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Customer'
responses:
201:
description: New Customer Created
# Poderíamos retornar alguma coisa, mas esse é o caso mais simples.
Voltando aos Códigos de Status HTTP, 201 quer dizer que um recurso foi criado no servidor.
Nesse caso não estamos esperando nenhum parâmetro, estamos esperando diretamente o json com tudo que precisamos dentro para criar um customer.
É completamente normal um método POST usar tanto o requestBody quanto parâmetros (query ou path). Cada elemento tem um propósito diferente e sua utilização depende do design da API. O requestBody é usado para enviar dados mais complexos no corpo da requisição e geralmente usado para criar ou atualizar recursos. Exemplos: JSON, XML, Yaml, ou outros formatos estruturados.
A situação que esta acontecendo acima é que o id esta sendo passado dentro do objeto customer, mas quem cdeveria criar o Id é o sistem, mas jajá vamos resolver isso.
Outra coisa que devemos ter em mente que RESTful é um padrão comumente aceito. Seguir os padrões depende da sua vontade. A idéia é seguir né?
É padrão devolver a localização do recurso no cabeçalho quando ele for criado no servidor então vamos implementar.
post:
summary: New Customer
description: Create a new customer
operationId: newCustomer
tags:
- Customer
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Customer'
responses:
201:
description: New Customer Created
##### Devolução no cabeçalho
headers:
location:
schema:
description: Location of the created resource
type: string
format: uri
example: http://example.com/v1/customers/{assignedIdValue}
# outro:
# schema:
# description:
# type: string
#####
Cada status pode ter:
- description (string) REQUIRED
- headers (header object)
- content (Conteúdo se necessário devolver)
- links
Uma coisa interessante é estamos falando de response, mas a request também pode e geralmente tem headers. Um exemplo é Content-Type (application/json) e o Bearer Token (authentication).
Podemos criar headers personalizados como vimos acima no caso do location.
Para resolver o problema do Id podemos trabalhar com readOnly e definir que esse campo não usado no request.
components:
schemas:
Customer:
type: object
properties:
id:
type: string
format: uuid
readOnly: true #<<<<
firstName:
maxLength: 100
minLength: 2
type: string
example: John
lastName:
maxLength: 100
minLength: 2
type: string
example: Thompson
address:
$ref: '#/components/schemas/Address'
description: customer object
Usando esse mesmo schema no request (post) e no response (get) vemos que omissão do id quando é uma request.
![]() | ![]() |
---|
O writeOnly faz exatamente o inverso. Por exemplo um password que esperando na request e não devolvemos no response.
Criando um PUT
Basicamente o PUT irá fazer uma atualização de um recurso existente. Dessa vez vamos utilizar o path /v1/customers/{Id}
pois queremos passar o Id do customer que será atualizado.
info:
#...
path:
#...
/v1/customers/{Id}:
get:
#...
put:
summary: Update Customer
description: Update customer by id.
tags:
- Customer
operationId: UpdateCustomerById
parameters:
# Esse parametro esta no components e é do tipo path
# Foi usados em exemplos anteriores
- $ref: '#/components/parameters/Id'
requestBody:
required: true
content:
application/json:
schema:
# Lembra que ele é readOnly para Id por isso estamos passando no path
$ref: '#/components/schemas/Customer'
responses:
204: # No Content: Requisição bem-sucedida, mas sem conteúdo para retornar.
description: Customer Updated
#...
#...
Criando um DELETE
Esta operação será praticamente a mesma do PUT a diferença que retornaremos a função que será executada no server será outra.
Uma coisa importante é que estamos retornando 200 pois é esperado que se um recurso for deletado não teriada nada para retornar.
path:
#...
/v1/customers/{Id}:
get:
#...
put:
#...
delete:
summary: Delete Customer
description: Delete customer by id.
tags:
- Customer
operationId: deleteCustomerById
parameters:
- $ref: '#/components/parameters/Id'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Customer'
responses:
200:
description: Customer Deleted
#...
#...
Utilizando Vários Status
Nem sempre teremos o status de sucesso. Pedir para deletar ou atualizar um recurso por um id não existente resultará em falha.
O openapi serve como uma documentação e a partir dela podemos gerar código que respeitem essa documentação, porém quem de fato implementa os valores de returno é quem desenvolveu o endpoint.
Vamos imaginar esse cenário.
paths:
/users:
get:
responses:
"404":
description: User not found
Essa será a descrição que voltará na mensagem? Deveria ser caso caso quem implementou a função que respeite a documentação do openapi. Porém, se quem implementou tivesse feito isso...
app.get('/users', (req, res) => {
const user = findUser(req.query.id);
if (!user) {
res.status(404).json({ error: 'Usuário não encontrado no sistema' });
}
});
Para o erro 404 viria a mensagem Usuário não encontrado no sistema
.
Os status são os valores esperados que a função retorne e o tipo de conteúdo, header, etc. É bom esclarecer isso para entender o OpenAPI não processa nada, mas serve como referência e a partir desta referência criamos padrões tanto para quem vai desenvolver e quem vai consumir.
Vamos colocar novos status que poderiam existir que já vamos exercitando outros tipos de status.
Os possíveis status irão aparecer na documentação.
openapi: 3.0.2
info:
###...
paths:
/v1/customers:
get:
###...
responses:
200:
description: List of Customers
content:
application/json:
schema:
$ref: '#/components/schemas/CustomerPagedList'
# Não precisamos de mais aqui por enquanto por ele vai entregar uma lista vazia ou não
post:
###...
responses:
201:
description: New Customer Created
headers:
location:
schema:
description: Location of the created resource
type: string
format: uri
400:
description: Bad Request
# O schema não foi respeitado
409:
description: Conflict
# Esta querendo incluir um usuário que já existe
/v1/customers/{Id}:
get:
###...
responses:
200:
description: Found Customer
content:
application/json:
schema:
$ref: '#/components/schemas/Customer'
404:
description: Not found
put:
###...
responses:
204:
description: Customer Updated
400:
description: Bad Request
404:
description: Not found
409:
description: Conflict
delete:
###...
responses:
200:
description: Customer Deleted
404:
# Caso o id passado não seja encontrado
description: Not found
###..
components:
###...
###...