Voltamos a falar de APIs, e voltamos a falar sobre especificações. Para continuar o assunto sobre Swagger, hoje vamos abordar o tema de uma maneira prática. Afinal, é com ela que a gente vê as principais vantagens de utilizar Swagger + Open API Spec, em relação aos concorrentes.
Não deixe de conferir uma abordagem prática ao RAML, no post "Ramilificando as suas APIs".
E mais uma vez vamos usar o nosso mini IMDB como exemplo.
Além disso, vamos aproveitar as ferramentas que o pessoal do Swagger disponibiliza gratuitamente. A começar pelo Swagger Editor. Não é necessário fazer nenhum tipo de cadastro ou instalação no momento, apenas abrir o editor através do link "Online Editor".
Ele trará um exemplo de especificação. Vamos limpar o editor e começar do zero.
Como explicado no post anterior, Swagger é um padrão de API Spec mas também é um conjunto de ferramentas que podem trabalhar tanto com Swagger Spec quanto com Open API Spec. Vamos começar o documento deixando clara a nossa escolha:
openapi: "3.0.1"
Confira a versão 3.0.1 do OAI.
A essa altura o editor já deve ter apontado alguns erros. Por exemplo, é obrigatória a presença de um atributo info e path. Vamos corrigí-los:
openapi: "3.0.1"
info:
title: Movies API
version: v1
paths:
/movies:
summary: A set of movies
Em info
, descrevemos o título da nossa API e sobre qual versão da mesma estamos falando (nesse caso, v1
). Em paths
, descreveremos os endpoints. Com a adição dessas duas propriedades, podemos progredir com a construção da API:
Conheça os atributos do OpenAPI Object.
Antes mesmo de adicionarmos os endpoints na propriedade paths
, vamos parar e descrever o schema dos nossos dados. Nesse exemplo, o uso da propriedade components
pode ser um "overhead", mas para APIs grandes, com vários endpoints, essa separação ajudará na legibilidade e reaproveitamento de código:
(...)
paths:
/movies:
summary: A set of movies
components:
schemas:
Movie:
properties:
id:
type: string
title:
type: string
description:
type: string
required:
- id
- title
type: object
Movie
é um objeto e possui três propriedades: id
, title
e description
, e as duas primeiras são obrigatórias. Você pode estar estranhando o id
ser do tipo string
. Nesse exemplo estamos assumindo que os identificadores são uuids
.
Saiba mais sobre Schema Objects.
Os principais endpoints que imaginamos para esse projeto são:
GET /movies
: Listagem de filmes;POST /movies
: Adição de um filme;GET /movies/{id}
: Detalhes de um filme;PUT /movies/{id}
: Atualização de um filme;DELETE /movies/{id}
: Remoção de um filme.A listagem é um bom ponto de partida.
Voltando à propriedade paths
, vamos melhorar a definição do /movies
:
(...)
paths:
/movies:
summary: A set of movies
get:
summary: Get a list of movies
responses:
'200':
description: Movie list response
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Movie'
Através do /movies
, dentro de paths
, estamos descrevendo o endpoint. Com o uso de get:
, declaramos que o endpoint aceitará o método GET
. Ainda abusando da indentação do YAML, deixamos claro que existe uma resposta 200
(sucesso), que o seu conteúdo é application/json
, e que dentro desse conteúdo haverá uma listagem de filmes.
Com o $ref
somos capazes de referenciar um Path Item Object do próprio arquivo que estamos trabalhando (nesse caso, o #/components/schemas/Movie
está referência ao bloco de YAML que criamos no capítulo anterior) ou até mesmo recursos externos. O que possibilita quebrar a spec em diferentes arquivos.
Se você já conhece o JSON Schema, essa notação não lhe parecerá estranha.
Ainda em /movies
, ao enviar um POST
, somos capazes de criar um novo filme. Se você já leu sobre REST sabe que essa prática é recomendada. Vamos adicionar o seguinte bloco abaixo do get
:
(...)
paths:
/movies:
(...)
post:
summary: Add a new movie to the set
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Movie'
responses:
'201':
description: Returns the new movie
content:
application/json:
schema:
$ref: '#/components/schemas/Movie'
Com o post:
, temos a necessidade do uso de uma nova propriedade chamada requestBody. É com ela que descreveremos quais parâmetros o usuário precisará passar para criar um filme. Já o responses
descreve o retorno de um 201
(created), com o recém criado filme como um JSON como conteúdo da resposta.
Opa! Temos um problema aqui! Nosso tipo Movie
tem um campo id
que é obrigatório por padrão. Esse campo não deveria ser enviado pelo cliente. Precisamos corrigir esse comportamento:
(...)
paths:
/movies:
(...)
post:
summary: Add a new movie to the set
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/NewMovie'
(...)
components:
schemas:
Movie:
allOf:
- $ref: '#/components/schemas/NewMovie'
- required:
- id
properties:
id:
type: string
NewMovie:
properties:
title:
type: string
description:
type: string
required:
- title
Com o uso do tipo NewMovie
, o request feito pelo usuário está descrito da maneira correta (sem id
). Note que em components
aproveitamos todas as características de NewMovie
, e adicionamos uma nova (ter id
) para o tipo Movie
. Isso tudo graças ao $ref
e ao allOf.
A essa altura, descrever os demais métodos e endpoints não será nenhum mistério. Portanto, vamos dar um "fast forward".
Segurança é importante, e tão importante quanto é deixar explícito para os desenvolvedores e usuários quais protocolos de autenticação/autorização que estão sendo praticados pela API. No nosso caso, vamos descrever o uso do protocolo OAuth 2.0, com o grant type
password
:
components:
(...)
securitySchemes:
OAuth2:
type: oauth2
flows:
password:
tokenUrl: https://example.com/oauth/token
scopes:
read: Grants read access
write: Grants write access
Falta anotar a propriedade security
em cada endpoint:
paths:
/movies:
summary: A set of movies
get:
summary: Get a list of movies
responses: (...)
security:
- OAuth2:
- read
post:
summary: Add a new movie to the set
requestBody: (...)
responses: (...)
security:
- OAuth2:
- write
Um ícone de cadeado deve ter aparecido no preview do Swagger Editor.
Com a especificação acima temos o suficiente para publicar a documentação da nossa API, e até mesmo gerar algum código client. O Swagger Editor quebrou um grande galho, auxiliando na escrita do documento, e é altamente recomendável o seu uso.
Um disclaimer especial vai para a versão do OpenAPI Spec que usamos nos exemplos. Infelizmente, nem toda ferramenta suporta a versão mais recente da especificação, logo, você terá que usar versões mais antigas caso queira utilizar as demais ferramentas que o Swagger disponibiliza.
O Swagger Code Generator é um projeto super bacana que interpreta a especificação e gera código para diferentes linguagens. Infelizmente, a versão estável do swagger-codegen só suporta até a versão 2.0
do OpenAPI Spec, e o snapshot mais recente ainda não possui uma gama grande de linguagens disponíveis.
Construir SDKs nunca foi tão fácil:
java -jar ~/Downloads/swagger-codegen-cli-3.0.0-20180314.142155-42.jar generate \
-i ~/Downloads/openapi.yaml \
-l java -o ~/Projects/java/mini-imdb
Se você quer usufruir dessa ferramenta, é recomendado que utilize as versões do OpenAPI Spec descritas no repositório do projeto: https://github.com/swagger-api/swagger-codegen#compatibility.
Com o Swagger UI, a partir da especificação recém criada, podemos gerar documentações elegantes e acessíveis ao nosso usuário final. O projeto é em NodeJS, e necessita de algum "Javascript-fu" para executá-lo.
A maneira mais fácil de usá-lo é através da plataforma SwaggerHub. Onde você pode criar um usuário gratuitamente e ter 1 (um) projeto publicado no plano free.
Confira o exemplo no SwaggerHub.
Vale notar que a documentação é "viva", ou seja, ela interage com os endpoints do seu serviço. Para que esse recurso funcione corretamente, você precisa anotar a propriedade servers
na especificação da API.
Leia mais sobre Server Object.
Se você já usou o Insomnia ou Postman, vai se sentir em casa com o Swagger Inspector.
O Inspector é uma GUI que auxilia nos testes de endpoints REST. Com ele, fica mais fácil inspecionar endpoints e validar o contrato e comportamento dos mesmos.
Confesso que acho o Postman muito mais eficiente nesse quesito. Mas infelizmente, o aplicativo só suporta até a versão 2.0
do Swagger Spec. Nada que impeça que você configure os requests para os endpoints manualmente.
A spec final você encontra aqui.
O ambiente em torno do OpenAPI é rico, e vale a pena considerar escrever suas próximas especificações de APIs com esse padrão. Se você, assim como eu, vem de outros formatos (como o RAML ou JSON Schema), não notará grande diferença na forma de escrita com o OAI.
É claro que não é só o pessoal do Swagger que cria ferramentas para Swagger Spec e OpenAPI Spec. Você pode conferir as diversas ferramentas criadas pela comunidade em Tools and Integrations.
Sem dúvida, falaremos sobre Python + Swagger nos próximos posts.
Até lá.