Modelando APIs REST com Swagger
Atualmente é bem comum que empresas utilizem APIs REST para a integração de aplicações, seja para consumir serviços de terceiros ou prover novos serviços.
Ao consumir uma API existente, precisamos conhecer as funcionalidades disponíveis e detalhes de como invocá-las: recursos, URIs, métodos, Content-Types e outras informações.
Ao prover uma nova API REST, além da implementação, há outras duas preocupações comuns: como modelar e documentar a API?
Ferramentas para modelagem e documentação de APIs REST
Em um Web Service do estilo SOAP temos o WSDL, que funciona como uma documentação (para máquinas) do serviço, facilitando a geração automatizada dos clientes que vão consumi-lo. Além disso, podemos modelar nosso serviço escrevendo o WSDL, em uma abordagem conhecida como Contract-First. Não é nada legível nem fácil de escrever, mas funciona. Só que no mundo dos Web Services REST não temos o WSDL. E agora?
Algumas ferramentas para nos auxiliar nessa questão foram criadas, e dentre elas temos: WSDL 2.0, WADL, API Blueprint, RAML e Swagger.
Neste post vamos abordar o Swagger, que é uma das principais ferramentas utilizadas para modelagem, documentação e geração de código para APIs do estilo REST.
Mas o que exatamente é o Swagger?
O Swagger é um projeto composto por algumas ferramentas que auxiliam o desenvolvedor de APIs REST em algumas tarefas como:
- Modelagem da API
- Geração de documentação (legível) da API
- Geração de códigos do Cliente e do Servidor, com suporte a várias linguagens de programação
Para isso, o Swagger especifica a OpenAPI, uma linguagem para descrição de contratos de APIs REST. A OpenAPI define um formato JSON com campos padronizados (através de um JSON Schema) para que você descreva recursos, modelo de dados, URIs, Content-Types, métodos HTTP aceitos e códigos de resposta. Também pode ser utilizado o formato YAML, que é um pouco mais legível e será usado nesse post.
Além da OpenAPI, o Swagger provê um ecossistema de ferramentas. As principais são:
- Swagger Editor – para a criação do contrato
- Swagger UI – para a publicação da documentação
- Swagger Codegen – para geração de “esqueletos” de servidores em mais de 10 tecnologias e de clientes em mais de 25 tecnologias diferentes
Nesse post, vamos focar na parte de modelagem de uma nova API. Futuramente teremos outro post focando na documentação de uma API já existente.
Modelando a API da Payfast
Para modelar nossa nova API, utilizaremos o Swagger Editor. Você pode instalá-lo localmente, executando uma aplicação NodeJS, ou utilizar a versão online em editor.swagger.io.
Vamos modelar a API da Payfast, uma aplicação de pagamentos bem simples que é estudada no curso SOA na prática.
Pra começar, devemos definir algumas informações iniciais, como a versão do Swagger que estamos usando:
swagger: '2.0'
O título, descrição e versão da API devem ser definidos:
info: title: Payfast API description: Pagamentos rápidos version: 1.0.0
Em host
, inserimos o endereço do servidor da API, em basePath
colocamos o contexto da aplicação e em schemes
informamos se a aplicação aceita HTTP e/ou HTTPS.
host: localhost:8080 basePath: /fj36-payfast/v1 schemes: - http - https
Defininindo o modelo de dados
De alguma forma, precisamos definir quais dados são recebidos e retornados pela API.
Na nossa API, recebemos dados de uma Transação, que tem um código, titular, data e valor. A partir disso, geramos um Pagamento com id, status e valor.
Em um WSDL, esse modelo de dados é definido através de um XML Schema (XSD). No caso do Swagger, o modelo de dados fica em um JSON Schema na seção definitions
do contrato.
De acordo com o JSON Schema, em type
podemos usar tipos primitivos de dados para números inteiros (integer
), números decimais (number
), textos (string
) e booleanos (boolean
). Esses tipos primitivos podem ser modificados com a propriedade format
. Para o tipo integer
temos os formatos int32
(32 bits) e int64
(64 bits, ou long). Para o number
, temos os formatos float
e double
. Não há um tipo específico para datas, então temos que utilizar uma string
com o formato date
(só data) ou date-time
(data e hora).
Além dos tipos primitivos, podemos definir objetos com um type
igual a object
. Esses objetos são compostos por várias outras propriedades, que ficam em properties
.
No nosso caso, o modelo de dados com os objetos Transacao
e Pagamento
ficaria algo como:
definitions: Transacao: type: object properties: codigo: type: string titular: type: string data: type: string format: date valor: type: number format: double Pagamento: type: object properties: id: type: integer format: int32 status: type: string valor: type: number format: double
Defininindo os recursos da API
Com o modelo de dados pronto, precisamos modelar os recursos da nossa API e as respectivas URIs. No Payfast, teremos o recurso Pagamento
acessível pela URI /pagamentos
.
Um POST em /pagamentos
cria um novo pagamento. Se o pagamento criado tiver o id 1, por exemplo, as informações estariam acessíveis na URI /pagamentos/1
.
Podemos fazer duas coisas com o nosso pagamento: para confirmá-lo, devemos enviar um PUT para /pagamentos/1
; para cancelá-lo, enviamos um DELETE.
No Swagger, as URIs devem ser descritas na seção paths
:
paths: /pagamentos: post: summary: Cria novo pagamento /pagamentos/{id}: put: summary: Confirma um pagamento delete: summary: Cancela um pagamento
Definindo os parâmetros de request
Para a URI /pagamentos
, que recebe um POST, é enviada uma transação no corpo da requisição, que deve estar no formato JSON. É feita uma referência ao modelo Transacao
definido anteriormente.
paths: /pagamentos: post: summary: Cria novo pagamento consumes: - application/json parameters: - in: body name: transacao required: true schema: $ref: '#/definitions/Transacao'
Já para a URI /pagamentos/{id}
, é definido um path parameter com o id do pagamento. Esse parâmetro pode ser descrito na seção parameters
, logo acima da seção paths
, e depois referenciado nos métodos.
parameters: pagamento-id: name: id in: path description: id do pagamento type: integer format: int32 required: true paths: /pagamentos: #código omitido... /pagamentos/{id}: put: summary: Confirma um pagamento parameters: - $ref: '#/parameters/pagamento-id' delete: summary: Cancela um pagamento parameters: - $ref: '#/parameters/pagamento-id'
Definindo os tipos de response
Definidos os parâmetros de request, precisamos modelar o response.
Depois da criação do pagamento, deve ser retornado um response com o status 201
(Created) juntamente com a URI do novo pagamento no header Location
e uma representação em JSON no corpo da resposta.
paths: /pagamentos: post: summary: Cria novo pagamento consumes: - application/json produces: - application/json #código omitido... responses: '201': description: Novo pagamento criado schema: $ref: '#/definitions/Pagamento' headers: Location: description: uri do novo pagamento type: string
Após a confirmação de um pagamento, é simplesmente retornado o status 200
(OK). O mesmo retorno acontece após um cancelamento.
/pagamentos/{id}: put: summary: Confirma um pagamento parameters: - $ref: '#/parameters/pagamento-id' responses: '200': description: 'Pagamento confirmado' delete: summary: Cancela um pagamento parameters: - $ref: '#/parameters/pagamento-id' responses: '200': description: 'Pagamento cancelado'
O contrato final do exemplo utilizado pode ser aberto no Swagger Editor em: bit.ly/swagger-editor-payfast-api
Perceba que no menu superior temos a opção Generate Server para gerar um esqueleto do servidor em Java (JAX-RS e Spring-MVC), PHP (Slim e Silex), Python (Flask), entre outras tecnologias. Há também a opção Generate Client, que gera clientes nessas tecnologias e em diversas outras.
Concluindo
A abordagem utilizada nesse post foi a conhecida como Contract-First ou API-First Development.
Modelamos a API pensando nos dados, nos recursos, URIs, métodos, parâmetros e respostas. Começamos a definição do serviço criando primeiro a API de comunicação, para só posteriormente pensar na implementação.
Esta abordagem gera um desacoplamento entre implementação e interface de uso, além de permitir que tando o lado cliente quanto o lado servidor possam iniciar seu desenvolvimento assim que a API estiver definida, mesmo sem uma implementação finalizada.
Uma outra abordagem (talvez mais comum) é começar pela implementação para só depois pensar na documentação e talvez realizar ajustes de modelagem. Essa abordagem é conhecida como Contract-Last e o uso dela com Swagger será abordado em outro post.
E você? Já usou o Swagger em algum projeto para modelar uma nova API, no estilo Contract-First? Conte-nos como foi a experiência!
Programação, Mobile, Front-end, Design & UX, Infraestrutura e Business
Poxa muito interessante! Estou desenvolvendo uma aplicação nesse sentido, mas com uma abordagem um pouco diferente.
Tiago, qual abordagem você adotou? Compartilhe com a gente!
Muito bom,
Agora fico no aguardo do post sobre a geração da documentação de um código existente.
Em breve teremos esse post sobre gerar doc. Fique ligada! 😉
Show, Alexandre e Rodrigo! Post muito detalhado, simples e direto!
O Swagger é realmente uma mão-na-roda para modelar APIs REST. Pra quem trabalha com equipes separadas de front e server-side, diria que é essencial; cada uma pode desenvolver independente, e no fim só “colam” as implementações reais.
Eu estou acompanhando o desenvolvimento do Wildfly Swarm, iniciativa para facilitar a implementação de microsserviços, que “brigará” com o Spring Boot, e lá já tem plugin para geração automática da documentação a API via Swagger. Mas segue a abordagem Contract-Last.
De qualquer forma, independente da abordagem, uma API bem documentada e projetada é meio caminho andado para o sucesso.
Parabéns pelo post!
Legal, Cácio!
Essas ferramentas para facilitar a implementação de microsserviços parecem promissoras! O Raphael Lacerda está preparando um post que vai falar sobre Wildfly Swarm, KumuluzEE, Vert.x e Spark.
Bem interessante! Iniciamos a construção de API mas começamos pela implementação e estamos no momento de documentar. To no aguardo do próximo post. : )
Usamos uma “app” do Django para gerar uma documentação a partir da nossa implementação.
https://github.com/marcgibbons/django-rest-swagger
Guilherme,
Muito interessante esse documentador de Swagger no Django.
No novo post, vamos documentar uma aplicação Java que usa JAX-RS. Somos javeiros! 🙂
Para os pythonistas/djangueiros, essa solução que você passou é muito boa!
Pois é.. uma hora sai o post
Olá Alexandre e Rodrigo, parabéns pelo material, realmente muito bom.
Eu fiz uma poc para estudar, fica aqui o link do meu Github, só falta colocar autenticação via token.
https://github.com/jroliveira/url-shortener/tree/master/src/Swagger
Este projeto é .net mas a documentação é gerada em Nodejs, mas para quem é de .net e quer gerar a documentação em .net, segue um pacote de encontrei integrar com Nancy.
https://www.nuget.org/packages/Nancy.Swagger/0.1.0-alpha3
Até mais.
Valeu, Júnior! Ótima dica essa sua! Interessante como não precisamos ficar presos a apenas uma plataforma, né?
Obrigado Alexandre, e sim não precisa ficar preso, no caso eu tinha implementado usando o pacote Nancy.Swagger, mas depois eu mudei para Nodejs para deixar a geração da documentação independente da tecnologia da API.
No dia de amanhã se eu quiser mudar a tecnologia da API para outra coisa eu não preciso me preocupar com a geração da documentação da forma que esta atualmente.
Fica a dica para o pessoa.
Senhores, muito bom o conteúdo do artigo. Meus parabéns!
Gostaria de aproveitar o espaço para fazer algumas perguntas. Tenho visto que cada dia mais as linguagens de programacão estão mais voltadas para o mundo REST/SOAP… a pergunta esta relacionada a como criar um front-end para interagir com essa API (REST ou SOAP) sem ficar preso à linguagem em que foi criado o meu back-end? Posso implementar totalmente com HTML, CSS e JavaScript e mesmo assim ser seguro?
Como SOAP e REST usam padrões (XML, HTTP, JSON, etc…) você pode trocar o backend sem problemas.
Segurança é um assunto à parte, mas vai passar no mínimo por HTTPS, certificados digitais e tokens.
Excelente post, Alexandre, parabéns 🙂
Uma dúvida: pensando no caso de alguém que está procurando uma ferramenta somente para documentação de uma nova API Rest, será que o Swagger seria a ferramenta ideal?
Pergunto isto pois, pelo que pouco que conheço dele, me parece um framework/ferramenta bastante robusto.
Qual sua opinião sobre isso?
Obrigado desde já e, novamente, parabéns pelo artigo claro e sucinto.
Bruno,
Depois desse, fizemos um post exatamente sobre documentação com Swagger:
http://blog.caelum.com.br/documentando-uma-api-jax-rs-com-swagger/
O Swagger é bastante (talvez mais) usado para documentar APIs REST.
Simples e direto ao ponto. Muito bom!
Parabéns!
Por favor! Façam um post de como usar o swagger com Vraptor! Preciso disso urgente!
Parabéns pelo artigo, estou utilizando-o para realizar a documentação dos serviços que estou criando. Conteúdo bem direto ao ponto.
Olá, gostaria de saber se posso criar testes de API já no swagger para que ele possa ser importado por alguma ferramenta de teste?
Olá, Alex.
A versão Pro do SOAP UI gera testes a partir de uma descricão Open API, que é usada pelo Swagger.
Vale a pena dar uma olhada:
https://smartbear.com/product/ready-api/soapui/overview/
muito interessante seus conteúdos gostei muito deles. Parabéns 🙂