sábado, 14 de setembro de 2019

O jogo dos 7 erros das APIs REST

engenhariaDeSoftware hashtagREST hashtagAPI Se você está criando ou vai criar uma API REST, leia e estude! E evite os principais problemas que “pululam” no Mercado.




O jogo dos 7 erros das APIs REST

Sou desenvolvedor de software há mais de 40 anos, tenho várias certificações técnicas, possuindo nível acadêmico de mestrado, tendo escrito vários livros e continuando a estudar estes assuntos diariamente, pois, não sei tudo. Aquele que pensa saber tudo, na verdade sabe nada.

Quem acha que sabe tudo, não aprendeu nada.
Aprender sempre.”

José Batista de Carvalho

Por que estou escrevendo isto? Porque a arrogância dos desenvolvedores modernos, ao pensar que dominam todas as técnicas, além de irritante é um sinal de que há algo muito errado com as instituições de ensino Brasileiras, que formam pessoas desprovidas de senso crítico.

E tenho encontrado essas pessoas desenvolvendo APIs REST, gerando verdadeiros absurdos. Então, eu pergunto: Custa admitir que desconhece API REST? É humilhação pedir ajuda? É o orgulho que as impede de aceitarem opiniões embasadas na teoria e prática?

API REST

Não é muito difícil encontrar boas definições e orientações sobre o assunto “API REST”. Para começar, humildemente, apresento minha contribuição:

Este meu post, de 2014, não esgota o assunto e nem tem esse objetivo, mas serve como guia inicial, apontando os primeiros passos e onde você pode encontrar mais embasamento teórico e prático sobre o assunto.

Outra excelente referência é https://restfulapi.net/

E existem vários livros relevantes, como estes (li todos):

  • REST API Design Rulebook: Designing Consistent RESTful Web Service Interfaces (English Edition), Mark Masse;
  • The REST API Design Handbook (English Edition),George Reese e Christian Reilly;
  • Hands-On RESTful API Design Patterns and Best Practices: Design, develop, and deploy highly adaptable, scalable, and secure RESTful web APIs (English Edition) por Harihara Subramanian e Pethuru Raj
Se você vai desenvolver uma API REST, deve seguir o padrão, caso contrário, os clientes futuros terão problemas em utilizá-la. E haverá problemas de manutenção, integração e publicação.
Muita gente confunde API REST com “expor métodos via HTTP”, que é o objetivo do SOAP (https://pt.wikipedia.org/wiki/SOAP).
Se você está criando ou vai criar uma API REST, leia e estude! E evite os principais problemas que “pululam” no Mercado. Coincidentemente, eu levantei 7 principais problemas. E aí? Toca algum sino? Sim! Parece o popular “jogo dos 7 erros”!
Jogo dos 7 erros
O “jogo dos 7 erros” é um passatempo popular em revistas e jornais. São apresentadas duas figuras, uma cópia da outra, só que com 7 erros, cabendo ao leitor descobri-los.
Com as APIs REST que tenho visto por ai, é a mesma coisa. Há 7 problemas graves e repetitivos, os quais listarei aqui, em ordem de quantidade de ocorrências:
1) Confundir "recurso" com "operação";
2) Utilizar incorretamente os métodos HTTP;
3) Expor a estrutura interna dos sistemas de backend;
4) Menosprezar a questão da Idempotência;
5) Mapear os recursos REST aos elementos internos do backend;
6) Utilizar parâmetros de querystring para identificar recursos;
7) Menosprezar as regras de retorno de status e de headers.

Vou explicar rapidamente quais são e o que você deve fazer para evitá-los.

Confundir "recurso" com "operação"

Uma API REST é um CRUD (Create-Read-Update-Delete) de recursos não-http, utilizando os métodos HTTP para isso.

Se você quer apenas invocar uma função remotamente, há vários protocolos e arquiteturas para isto, como RPC – Remote Procedure Call (https://en.wikipedia.org/wiki/Remote_procedure_call).
Uma API REST é composta por “rotas” padronizadas e recursos identificados por URI. Um exemplo de que você caiu nesse erro é ter algo como uma chamada com URL: “calcularJuros”, ou então fazer várias chamadas para realizar uma operação.
Um request REST é ATÔMICO e “Stateless”, ou seja, é executado de uma só vez e o servidor não guarda estado entre os requests.

Utilizar incorretamente os métodos HTTP
Em REST, a semântica da operação é dada pelo Método HTTP utilizado:
  • GET: Obter recurso (ou recursos). É um método idempotente, que usa a URI para obter o recurso. É para ser idempotente;
  • PUT: Criar ou alterar um recurso. Você passa a URI e no corpo do request vai a representação do recurso (JSON, XML etc). Há controvérsias, mas a princípio seria idempotente;
  • POST: Criar um recurso, sem passar identificador, com a representação no corpo do request. No retorno, deve vir a URI do novo recurso, dentro dos HEADERS. Não é idempotente;
  • DELETE: Remover um recurso do backend, passando a URI. Deveria ser idempotente;

Sabe o que significa idempotência?

“A request method is considered "idempotent" if the intended effect on the server of multiple identical requests with that method is the same as the effect for a single such request. ”

Um método idempotente é aquele que o efeito de múltiplos requests com o mesmo método é o mesmo que um único request daquele método.
Tem gente que só usa GET para tudo, tem gente que só usa POST para tudo, inclusive para eliminar recursos! Já vi gente utilizar GET para alterar, eliminar e criar registros!
Use os métodos HTTP como parte da semântica CRUD da sua API REST!
Expor a estrutura interna dos sistemas de backend
Sabe o que significa “REST”? Significa: “REpresentational State Transfer”, ou seja: “Transferência de Estado Representacional”. Representacional de quê? De recursos no backend!
Você transfere representações de recursos, e estas representações não devem e não podem corresponder exatamente às estruturas internas do software de backend, como tabelas de bancos de dados, por exemplo.
Isto faz parte das 6 restrições REST:
  1. Uniform interface
  2. Client–server
  3. Stateless
  4. Cacheable
  5. Layered system
  6. Code on demand (optional)

Ao utilizar chaves primárias de bancos de dados simples ou compostas, ou tabelas individuais mapeadas como recursos, ou ainda strings de conexão JDBC (acredite eu vi isso), você está expondo a estrutura interna do servidor, violando o conceito de “client-server”. Por exemplo, pode ser que o cliente esteja conversando com um “cache” e não diretamente com o Servidor.

Ao acoplar o cliente às estruturas internas do backend, você está propagando atualizações, aumentando o “britleness” (https://en.wikipedia.org/wiki/Software_brittleness) da sua aplicação e levando problemas para os seus clientes.

Recursos devem ser representações independentes, e sua cardinalidade não precisa ser 1x1 com os elementos internos do sistema de backend. O cliente não deve ter a menor ideia da estrutura e organização interna do backend.

Menosprezar a questão da Idempotência
Há operações “seguras” e operações inseguras. Operações inseguras são aquelas que mudam o estado interno do backend ao serem executadas ou repetidas, podendo gerar inconsistências.
Já vimos o conceito de idempotência, e os métodos HTTP que servem às operações “seguras”:
+---------+------+------------+
| Method  | Safe | Idempotent |
+---------+------+------------+
| CONNECT | no   | no         |
| DELETE  | no   | yes        |
| GET     | yes  | yes        |
| HEAD    | yes  | yes        |
| OPTIONS | yes  | yes        |
| POST    | no   | no         |
| PUT     | no   | yes        |
| TRACE   | yes  | yes        |
+---------+------+------------+ 

Veja que o método PUT não é seguro, mas é idempotente!

E qual é o problema? Ao usar GET para tudo, você está indicando que a operação é segura e idempotente quando, na verdade, pode não ser. Tem gente que usa GET para incluir, alterar ou excluir coisas no backend. GET pode ser cacheado e até guardado como “favorito”. Você está indicando que a operação é segura e idempotente, quando, na verdade não é.

E usar POST para tudo? Até para ler uma lista de recursos? Você está indicando que a operação é insegura e não idempotente, quando, na verdade, ela é!

Mapear os recursos REST aos elementos internos do backend

Pode parecer o mesmo erro que: “Expor a estrutura interna dos sistemas de backend”, mas não é. Na verdade, é um subconjunto especial daquele erro. O primeiro é expor elementos e estrutura interna do backend, como: Senhas de acesso, relacionamentos entre tabelas, chaves estrangeiras e primárias, strings de conexão etc. Este é sobre mapear diretamente cada elemento interno do backend a um recurso REST.
O backend pode possuir vários elementos, sejam eles tabelas de bancos de dados, coleções de documentos No-SQL ou até mesmo chamadas de subrotinas. Ao mapear 1 para 1 cada elemento interno do backend a um recurso REST, você está acoplando o cliente à estrutura interna do backend, aumentando o “britleness” e complicando a sua API REST desnecessariamente.
Lembre-se: Um cliente vai fazer CRUD com representações de recursos. Ao quê essas representações estão associadas, não interessa. Um recurso pode significar escrever em dois ou três databases diferentes, gerar arquivos ou até mesmo gravar mensagens em filas assíncronas. Mas o cliente tem que lidar apenas com a representação simples do recurso.
Se você criou 3 recursos em sua API, dependendo da semântica, terá que criar várias rotas REST para cada um (GET, PUT, POST e DELETE). Sua API será bastante complicada. Se você está lidando com 3 tabelas de um database, e as operações são interligadas, não faria mais sentido juntar tudo em um único recurso? Pense nisso.
Utilizar parâmetros de querystring para identificar recursos
Veja só esta query-string:

“/api/empregado?matricula=1029”

O que há de errado com ela? Está usando parâmetros de query-string para identificar recursos, e isto não é permitido com REST.
Os recursos são identificados por sua URI, e somente através dela são manipulados. É exatamente como funciona com páginas HTML, por exemplo. Vamos ver a forma correta daquela URI:

“/api/empregado/1029”

Agora, se eu quiser ver a lista de todos os empregados:

“/api/empregado”

E se eu quiser apenas a foto de um determinado empregado:
“/api/empregado/1029/foto”

Parâmetros de query-string em REST são utilizados apenas para filtrar ou paginar um recurso extenso, por exemplo, todos os empregados do Rio de Janeiro:

“/api/empregado?filter=”UF=RJ””

Se eu quiser paginar, por exemplo, pegar de 50 em 50 empregados a partir do 51:
“/api/empregado?offset=51&limit=50”

Entendeu? Você deve utilizar a URI para identificar recursos e jamais query-string. A URI pode ser colocada nos “favoritos” e o resultado pode ser guardado em “cache”!

Menosprezar as regras de retorno de status e de headers
Em uma API REST, o HTTP Status deveria ser a informação mais importante, afinal de contas o próprio método HTTP já diz a semântica da operação. Por exemplo, qual seria o HTTP Status para um GET?
  • 200: Se o recurso existe e o cliente tem autorização para acessá-lo;
  • 404: Se o recurso não existe com aquela URI;
  • 401: Se o cliente não se autenticou;
  • 403: Se o cliente se autenticou, mas não tem direito de acessar o recurso;
E os códigos para uma operação de criar recursos com POST? Além do 401 e 403?
  • 201: Created;

E se for um DELETE? E um PUT?
  • 204: No content – Sem nenhum conteúdo no corpo da resposta;
E se minha API não permite alterações ou deleções? Se a sua API não permite determinado método:
  • 405: Method not allowed;
No caso de criação de recurso com POST, você não sabe a URI que será criada, portanto, faz sentido retornar a URI dentro do corpo da resposta, mas a URL completa do recurso (incluindo a URI) deve vir no header “location”, da resposta.

É claro que essas orientações não esgotam o assunto e que você deve estudar como é o retorno das rotas de uma API REST. Tem este bom artigo aqui: https://restfulapi.net/http-status-codes/
Vejo muita gente retornando HTTP STATUS 200 para tudo, e um monte de baboseiras no corpo da resposta. Existem alguns padrões que você pode utilizar, como o JSEND (https://github.com/omniti-labs/jsend), mas o importante mesmo é saber utilizar o HTTP Status e os headers adequadamente.

Conclusão

REST é uma arquitetura de comunicação conhecida internacionalmente, e, se sua API for aderente a ela, qualquer pessoa no mundo conseguira entendê-la e utilizá-la adequadamente, sem ajuda alguma. Você pode até expor sua API utilizando bibliotecas como o Swagger (https://swagger.io/).

Porém, se insistir nesse “jogo dos 7 erros”, além de passar vergonha, vai complicar demasiadamente a vida dos seus clientes, colocando sua bela API no caminho do fracasso.

Cleuton Sampaio, M.Sc.









Nenhum comentário:

Postar um comentário