Skip to main content

Introdução para Especificação OpenAPI

O repositório git, a documentação de learning e os exemplos ajudam bastante a iniciar nessa jornada.

É importante ter conhecimento de YAML e JSON para entender as especificações.

Vamos partir para a definição mais simples de uma API.

alt text

Nem todos os campos são necessários mas é boa prática declarar pelo menos os campos abaixos em info.

openapi: '3.0.3'
info:
title: OpenAPI Learning
version: '1.0.0'
description: 'Simple API to start learning'
paths: {}

No repositório, dentro da pasta versions, podemos ver uma documentação bem rica sobre o objeto de definição da API, ou seja, a estrutura que se espera do yaml file que vamos criar. Também podemos ver essa mesma documentação no site so swagger https://swagger.io/specification/. Durante o processo de aprendizagem é necessário estar bem familiarizado com esta página.

Este é o top level das especificação e em cada um desse types podemos ver como funciona cada um desses objetos.

alt text

info (Required)

Documentação

Fornece os metadados sobre a API. É boa prática ter boas informações para a documentação da API. Vamos preencher o máximo que temos para ver o que podemos fazer. Dentro do Objeto de info temos mais dois objetos (contact e license).

openapi: '3.0.3'
info:
title: OpenAPI Learning # Obrigatório
summary: First API Specs.
version: '1.0.0' # Obrigatório
description: 'Simple API to start learning'
# Termos de serviço também esepra uma URL
termsOfService: https://buymeacoffee.com/davidpuziol/ #:D
contact: # Objeto de contact
name: API Support Learning
url: https://devsecops.puziol.com.br/docs/category/openapi
email: [email protected]
license: # Objeto de license
name: Apache 2.0
url: https://www.apache.org/licenses/LICENSE-2.0.html
paths: {}

alt text

servers (Optional)

É uma maneira de fornecer aos usuários finais informações sobre onde a API esta disponível. Cada objeto server dentro possui url (obrigatório), descrição e variáveis.

openapi: '3.0.3'
### INFO
info:
title: OpenAPI Learning
summary: First API Specs.
version: '1.0.0'
description: 'Simple API to start learning'
termsOfService: https://buymeacoffee.com/davidpuziol/ #:D
contact:
name: API Support Learning
url: https://devsecops.puziol.com.br/docs/category/openapi
email: [email protected]
license:
name: Apache 2.0
url: https://www.apache.org/licenses/LICENSE-2.0.html
### SERVERS
servers:
- url: https://development.example.com/v1
description: Development server
- url: https://staging.example.com/v1
description: Staging server
- url: https://api.example.com/v1
description: Production server
paths: {}

alt text

É possível definir variáveis para mas é menos usual. Se fossemos fazer o mesmo exemplo anterior poderíamos fazer assim.

servers:
- url: https://api.example.com/v1
description: Production server
- url: https://{environment}.example.com/{basePath}
description: The Server for Each Environment
variables:
environment:
description: This value select api to specific environment at example.com`
default: development
enum:
- 'development'
- 'staging'
basePath:
default: v1

alt text

paths (Required)

Chegamos na API e vamos pra onde? É necessário fornecer alguns os caminhos (endpoints).

Mesmo que vazio o path precisa ser definido portanto temos o paths: {} para satisfazer o schema do OpenAPI.

Aqui é onde tudo acontece, então é necessário dedicar um maior para aprender. O objeto em sí é uma matriz de objetos do tipos path items.

paths:
/: # Objeto do tipo path items que possui muitas definições dentro
/products: # path item
# Qualquer caso que tenha product/1 product/2 irá cair aqui
/product/{productId}: # path item

Observe que NÃO é um array de objeto, mas uma matriz, se fosse array seria como o exemplo abaixo. Não é array pois um array é posicional e uma matriz não.

# Só para entender a diferença
path:
- /: # Objeto vazio associado à chave "/"
- /products: # Objeto vazio associado à chave "/products"
- /product/{productId}: # Objeto vazio associado à chave "/product/productID"

Agora vamos para o que de fato define cada um dos paths que é o objeto path item.

Path Items

O Path Item se resume ao objeto que irá definir um path. Em cada path temos sua descrição, um id de referencia, parâmetros e todos e todos os verbos que ele espera receber, etc.

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

Um objeto de operação precisa de uma resposta, não vamos entrar ainda em detalhes sobre isso, apenas vamos construir um path com o mínimo necessário para uma operação e mais adiante iremos nos apronfundar no objeto de operações.

##info:
# Removido para ficar só o que interessa por enquanto
## ...
paths:
/v1/customers:
get:
responses: # Dentro de um objeto de operação temos as possíveis respostas
'200':
description: List of Customers

alt text

Podemos ter várias respostas para uma mesma operação. Não vamos entrar em detalhes sobre os dados ainda. As respostas são baseadas nos códigos que apresentados anteriormente.

Se observou usamos /v1/customer agora. Isso é uma convenção. Poderíamos declarar /customer, mas se um dia quisermos ter duas versões desse mesmo path será muito mais trabalhoso de mudar, então procure sempre utilizar /versão/path.

Por que usar /v1 é uma convenção comum?

  • Controle de versões explícito: Adicionar a versão (v1, v2, etc.) ao caminho da API deixa claro para os consumidores qual versão eles estão usando. Isso ajuda a evitar breaking changes (mudanças que quebram compatibilidade) quando novas versões são lançadas.
  • Facilidade de transição: Quando uma nova versão da API é lançada, a antiga pode continuar ativa (/v1) enquanto a nova (/v2) é adotada gradualmente pelos clientes.
  • Desacoplamento de versão e implementação: O backend pode mudar sem afetar os consumidores da API, desde que mantenha a versão correspondente no endpoint.
  • Padrão amplamente utilizado: Muitos frameworks, ferramentas e documentações assumem que uma API tem versão nos caminhos. Isso facilita integração e familiaridade para desenvolvedores.
  • Separação de responsabilidade: Manter versões explícitas permite que diferentes versões sejam gerenciadas por equipes distintas ou até mesmo em infraestruturas separadas.
  • É util se a API for evoluir rapidamente.

Existem outras técnicas para resolver esse caso, mas faz mal? Não? então mantém!