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.

Modelo de dados da API do Payfast

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.

Máquina de estados da API do Payfast

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!

Tags: ,

17 Comentários

  1. Tiago Silva Pereira 23/02/2016 at 14:45 #

    Poxa muito interessante! Estou desenvolvendo uma aplicação nesse sentido, mas com uma abordagem um pouco diferente.

  2. Alexandre Aquiles 23/02/2016 at 16:22 #

    Tiago, qual abordagem você adotou? Compartilhe com a gente!

  3. Gracyane Oliveira 23/02/2016 at 23:01 #

    Muito bom,
    Agora fico no aguardo do post sobre a geração da documentação de um código existente.

  4. Alexandre Aquiles 23/02/2016 at 23:36 #

    Em breve teremos esse post sobre gerar doc. Fique ligada! 😉

  5. Cácio Costa 24/02/2016 at 09:24 #

    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!

  6. Alexandre Aquiles 24/02/2016 at 13:19 #

    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.

  7. Guilherme Bazilio 25/02/2016 at 10:13 #

    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

  8. Alexandre Aquiles 25/02/2016 at 13:12 #

    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!

  9. Raphael Lacerda 29/02/2016 at 14:38 #

    Pois é.. uma hora sai o post

  10. Júnior Oliveira 07/03/2016 at 11:38 #

    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.

  11. Alexandre Aquiles 07/03/2016 at 14:42 #

    Valeu, Júnior! Ótima dica essa sua! Interessante como não precisamos ficar presos a apenas uma plataforma, né?

  12. Júnior Oliveira 07/03/2016 at 18:23 #

    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.

  13. Ricardo Lopes 08/04/2016 at 05:01 #

    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?

  14. Alexandre Aquiles 08/04/2016 at 07:34 #

    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.

  15. Bruno Dadalt Zambiazi 22/02/2017 at 23:03 #

    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.

  16. Alexandre Aquiles 23/02/2017 at 07:05 #

    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.

Deixe uma resposta