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 moviesEm 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: objectMovie é 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:
- titleCom 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 accessFalta 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:
- writeUm í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-imdbSe 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á.