LinkApi Ebook APIs RESTful
LinkApi Ebook APIs RESTful
DE APIS RESTFUL
por Thiago Lima
Conteúdo
18APIs
para construir uma aplicação. E todo ano surgem
1 bilhão de dólares
até 2020, com taxa de crescimento anual acima de 30%.
Pense no seguinte cenário como programador, imagine que você tem um grande
conhecimento em gastronomia, e então você cria um banco de dados para
armazenar esses dados. Mas depois você percebe que outras pessoas podem
se beneficiar disso. Pronto, é aí que entram as APIs, nesse caso, você poderia
desenvolver uma interface, ou seja sua API, para que outros desenvolvedores
pudessem criar aplicativos ao redor desse conhecimento, por exemplo:
recomendação de vinhos, ranking de receitas, e por aí vai.
Database
5
Entendendo uma requisição
O endpoint
A URL nada mais é que o caminho para fazer a requisição, porém é interessante
ressaltar que ela segue a seguinte estrutura:
Base URL
Esse é o início da URL da requisição, aqui você basicamente falará a informação de
domínio que se repete em qualquer requisição. Por exemplo:
https://api.minhagastronomia.com
Resource ou Path
O recurso é o tipo de informação que você está buscando, ou seja, vamos simular que
estamos buscando saber sobre vinhos, então acrescentamos o recurso vinhos:
https://api.minhagastronomia.com/vinhos
Query String
A query string são os parâmetros daquela requisição, então, se eu quisesse
saber os melhores vinhos da região sul do Brasil, eu incluiria esses parâmetros
?pais=brasil®iao=sul e nossa URL ficaria assim:
https://api.minhagastronomia.com/vinhos?pais=brasil®iao=sul
Como podem ver acima, por se tratar de parâmetros da URL você usa a “?” e caso
queira utilizar mais de um parâmetro você utiliza o “&”.
Observação: A Query String não é somente utilizada para filtros, ela pode ser utilizada
como parâmetros de paginação, versionamento, ordenação e muito mais.
6
O método
O método te ajuda a informar o tipo de ação que você está fazendo naquela requisição.
Dentre os principais métodos, temos:
Headers
Headers ou cabeçalhos permitem que você envie informações adicionais na
requisição. Ele pode ser utilizado para inúmeras funções, como: autenticação,
formatação de objeto, e muito mais.
Para utilizá-lo é simples você coloca a propriedade, seguido dois pontos e o valor, tudo
entre aspas, exemplo:
"Authorization: token123242343534"
Body
O body é o corpo da mensagem que você quer enviar na requisição. Ele é utilizado
somente nos métodos de POST, PUT, PATCH, ou seja, ele contém o dado a ser
processado pela API, e por isso ele não é necessário em métodos de leitura de dados.
Os códigos mais utilizados para as respostas de uma requisição são o 200 (OK), o 201
(created), o 204 (no content), o 404 (not found), o 400 (bad request), e 500 (internal
server error).
7
Autenticação
Obviamente não podemos falar de APIs sem segurança, afinal estamos falando da
WEB. Como principais métodos de autenticação de APIs, temos:
Basic authentication
Baseado em usuário e senha codificados em Base64 e utilizado no header da
requisição.
Secret token
Token de acesso que pode ser limitado a escopo, e que é enviado na requisição pelo
Header ou pela Query String. Nesse caso temos padrões famosos como oAuth e JWT.
Se quiser entender ainda mais sobre os requests, recomendo olhar uma API de testes
para você conseguir “brincar” com algumas chamadas, como por exemplo o
https://reqres.in.
Pronto! Agora você já sabe o básico sobre o funcionamento de uma API, e um pouco
do que ela é capaz de fazer.
8
Métodos e códigos de status
do protocolo HTTP
Quando falamos de métodos eles basicamente são ações permitidas dentro de uma
API. E ao falar de códigos de status, eles são os retornos padrões de uma API. E ambos
fazem parte das definições do protocolo HTTP.
Agora sim, vamos nos aprofundar mais sobre esses métodos e códigos de status
permitidos na utilização desse protocolo.
Métodos
Vamos falar basicamente dos 5 principais, e que provavelmente será os que você irá
passar 99% do seu tempo como desenvolvedor.
O método GET
Esse será o método que você mais irá utilizar, pois ele é utilizado para buscar dados em
APIs. A ideia dele é simples, você utiliza uma estrutura de Query String para enviar os
parâmetros da sua consulta e o servidor retorna os dados em caso de sucesso, ou caso
você não envie a Query String na URL, a API irá retornar todos os dados sem o filtro.
Vamos continuar o exemplo de uma API que traz dados sobre gastronomia e sua URL
base é
https://api.minhagastronomia.com
9
Como desenvolvedor eu gostaria de consultar dados sobre vinhos brancos de até 100
reais. Então eu faria a requisição
GET https://api.minhagastronomia.com/vinhos?ate_preco=100&tipo=branco
Lembrando que a API teria que fornecer os parâmetros da Query String (ate_preco e
tipo) em sua documentação para utilização na requisição.
Ou então, imaginem que eu já tenho a lista de vinhos devido a minha última requisição,
e quero me aprofundar mais em um vinho específico que tem o identificador único 22.
Nesse caso, eu faria a requisição abaixo, no qual, o 22 seria o valor do parâmetro {id} da
URL. Nesse caso, não é utilizada uma Query String, e sim um parâmetro de URL, pois o
{id} é obrigatório.
GET https://api.minhagastronomia.com/vinhos/22
O método POST
Agora pense que você não encontrou um vinho que gosta, e gostaria de cadastrar esse
vinho nessa API. Nesse caso, você utilizaria o método POST, e enviaria os dados desse
novo vinho no corpo (Body) da requisição, por exemplo:
POST https://api.minhagastronomia.com/vinhos
Body
{
"nome": "Maycas Reserva Sumaq Chardonnay 2016",
"preco": 49,
"pais": "Chile",
"recomendado": "peixe, frutos do mar",
"tipo": "branco"
}
10
Os métodos PUT e PATCH
O método PUT é utilizado em cenários de criação e atualização de dados, ou seja, se
eu enviasse o vinho e ele já existisse, então ele apenas seria atualizado, caso contrário,
seria criado um novo vinho.
Por sua vez, o método PATCH serve para atualizações parciais daquele registro, ou
seja, vamos imaginar que aquele vinho cadastrado gerou o identificador 55, e que
agora eu gostaria de atualizar apenas o preço dele, a requisição ficaria dessa forma:
PATCH https://api.minhagastronomia.com/vinhos/55
Body
{
"preco": 49,
}
O método DELETE
Como o próprio nome já diz, o método DELETE é o responsável por deletar os
registros. Portanto, supondo que você queira deletar o mesmo vinho que você acabou
de atualizar, você faria a seguinte requisição:
DELETE https://api.minhagastronomia.com/vinhos/55
Outros métodos
Existem também outros métodos como OPTIONS, HEAD e TRACE, porém eles são
raramente são utilizados.
11
Códigos de status
Agora vamos aos códigos de status permitidos pelo HTTP, e que podem ser agrupados
por tipo. Esses códigos são retornados pelo servidor, e servem para informar ao
desenvolvedor o que aconteceu após a sua requisição.
Grupo 1
Grupo de status que dá respostas informativas, existem
poucas e raramente são utilizadas.
Grupo 2
Grupo de status utilizado em caso de sucesso na requisição.
Os mais utilizados desse grupo são:
200 OK — Código mais utilizado, e que indica que a requisição foi processada
com sucesso.
201 Created — Indica que a requisição foi bem sucedida e que um novo registro
foi criado como resultado. Resposta utilizada em requisições do método POST.
Grupo 3
Define respostas de redirecionamento. São utilizadas com o intuito de informar o
cliente sobre mudanças na requisição e o redirecionamento para uma nova URL. Esses
são os mais comuns:
12
Grupo 4
Preste bastante atenção aqui, pois esse grupo informa os erros do lado do cliente.
Em outras palavras, caso você monte a requisição de forma incorreta, o servidor irá
retornar um erro desse grupo. Vamos aos principais:
400 Bad Request— Essa resposta significa que o servidor não consegue
entender sua requisição, pois existe uma sintaxe ou estrutura inválida.
401 Unauthorized — Erro que informa que existe uma camada de segurança no
recurso solicitado ao servidor, e que você não está utilizando as credenciais corretas
nessa requisição. Lembrando que as credenciais podem ser algo como usuário e senha,
token, etc. E tudo vai depender da API que você estará consumindo.
404 Not Found — Código que informa que o servidor não encontra o recurso
solicitado pelo cliente.
429 Too Many Requests — Não é tão comum, mas pode ser utilizado para
informar ao cliente que ele excedeu o limite permitido de requisições.
Grupo 5
Tão importante quanto o grupo 4, nesse grupo estão os erros do lado do servidor. Em
outras palavras, pensem que nesse caso a requisição foi feita corretamente pelo lado
do cliente, porém houve um erro de processamento no servidor. Vamos aos principais
códigos desse grupo:
500 Internal Server Error — Erro mais genérico desse grupo e muito utilizado.
Ele informa que o servidor encontrou um cenário inesperado de erro que não soube
tratar, e por isso não conseguiu retornar uma resposta na requisição do cliente.
13
Segurança em APIs RESTful
Nesse capítulo vou abordar um tema polêmico e importante sobre APIs — Segurança.
Já ouvi muita empresa perguntar “mas esse negócio de APIs é seguro mesmo?”
Autenticação vs Autorização
Autenticação é como se você fosse para uma festa, e o segurança exigisse suas
credenciais como nome na lista e RG para liberar sua entrada.
Basic Authentication
Basic authentication é um esquema de autenticação bem simples especificado no
protocolo HTTP.
O cliente envia uma requisição com o header (Authorization) que contém a palavra
Basic e o nome de usuário e senha, separados por dois pontos no formato de base64.
14
Por exemplo, para autorizar o usuário thiago com senha thiago@123, o client enviaria
na requisição, o seguinte header:
São comuns casos em que é substituído o usuário e senha por um token, mas isso é
exceção e não é recomendado pela especificação.
API Keys
API Keys é um método de autenticação que teoricamente veio para resolver alguns
problemas do modelo de Basic Authentication.
A ideia do método de API Keys é simples, o servidor gera uma chave de acesso única
para o client, e para utilizar a API, o client envia essa chave única em toda requisição.
É um método bem simples e rápido de ser implementado, e durante anos foi o método
mais utilizado pelos desenvolvedores.
A grande questão é que esse método serve apenas para autenticação, e não para
autorização, ou seja, em teoria um usuário com uma Key válida tem acesso a todas as
operações de uma API.
15
Além disso, ela funciona como qualquer requisição HTTP, e se algum ponto de rede
estiver inseguro, ela pode ser facilmente interceptada e utilizada para acesso indevido
da API.
Normalmente essa key é utilizada no header, por exemplo, imagine que você recebeu
do servidor a seguinte key: “thi123456”, nesse sentido para eu me autenticar na API eu
faria uma requisição com o seguinte header:
Api-key: thi123456
Vale ressaltar que o parâmetro de header pode variar de acordo com a API, ou seja,
pode ser “x-Api-key” por exemplo, ou então, pode ser que o envio dessa key seja via
Query String, e não pelo header, algo menos seguro ainda, pois fica extremamente
visível para quem vai atacar.
OAuth
O OAuth está na sua versão 2.0, e não é apenas um método de autenticação, e sim um
protocolo completo com diversas especificações de segurança.
• Resource Server: servidor que possui os recursos, e por sua vez, recebe as
requisições.
• Authorization Server: servidor que gera tokens de acesso para permitir que o
client acesse os recursos autorizados pelo resource owner.
Para entender melhor, vamos supor que você desenvolveu uma aplicação que utiliza
dados do usuário do Facebook, então vamos simular como seria um fluxo básico de
autenticação via OAuth 2.0:
16
1. O usuário acessa seu site ou app que teria um botão de “integre ao facebook”,
sendo que o seu site ou app seria o client.
17
SAML
Temos também o SAML, ou Security Assertion Markup Language. Que é um padrão
aberto que permite que provedores de identidade (IDP) passem credenciais de
autorização para provedores de serviços (SP). Em outras palavras, o usuário pode usar
as mesmas credenciais para entrar em aplicações diferentes.
Na prática, o SAML ativa o Single Sign-On (SSO), algo bem interessante para a
experiência dos usuários, pois eles fazem o login uma única vez e consegue navegar em
todos as aplicações com o padrão implementado.
Não vejo outro cenário de aplicação do SAML além de casos que o login único seja
premissa, pois ele utiliza o XML na comunicação, que já é algo ultrapassado para o
desenvolvimento WEB que estamos vivendo atualmente.
GET https://api.test.com/orders/?apiKey=thiago1234
Isso facilita muito a vida de alguém com más intenções na sua API.
18
Considere a utilização de OAuth
O OAuth hoje é sem dúvidas a especificação mais completa para APIs RESTful, por
isso, utilize tudo que há de melhor no OAuth, apenas tomem muito cuidado para não
deixar a implementação muito complexa e impactar negativamente a experiência do
desenvolvedor que irá consumir sua API.
Além disso, em casos que o client ultrapasse o limite de requisições, você retorna o
status code 429 — Too Many Requests.
Timestamp na requisição
Se for uma API em que segurança é um
ponto crítico, uma boa dica é criar um header
customizado com o timestamp. Assim o server
pode comparar o timestamp atual, e aceitar
apenas as requisições que estiverem próximas a
um período de tempo (por exemplo: 2 minutos).
19
Valide o parâmetros da requisição
Por questões de segurança, sempre valide os paramêtros da requisição antes da
execução da lógica do negócio.
Além disso, para não impactar negativamente a experiência de quem não está mal
intencionado, retorne mensagens de erros claras sobre o assunto, e disponibilize
exemplos da requisição feita corretamente.
API Gateway
Customer
microservice
Security
Client Routing
Protocol
Translation
Payments
Audit microservice
20
Versionando APIs RESTful
Existem diversas maneiras de versionar sua API, e nesse capítulo vou listar as
principais formas.
https://api.minhagastronomia.com
GET https://api-v1.minhagastronomia/vinhos
Notem que logo após o api, eu coloquei o “-v1”, que especifica a versão, e nesse caso
quem for consumir pode alterar apenas o subdomínio na requisição.
No modelo de path você especifica a versão após a base url, por exemplo:
GET https://api.minhagastronomia/v1/vinhos
Essa é uma das abordagens mais utilizadas, pois além de dar um visual mais clean
na URL, facilita a navegação para outras versões da API, ou seja, é mais dev-friendly
comparado a outras abordagens.
21
Tem quem é mais criativo, e prefere especificar a versão via query string, por
exemplo:
GET https://api.minhagastronomia/vinhos?version=1.0&pais=brasil
Notem que na URL eu coloquei o parâmetro via query string “version” e especifiquei a
versão 1.0 da API.
Essa é uma abordagem que já foi muito utilizada, mas que caiu em desuso, pois além
de prejudicar a navegação para outras versões, a legibilidade da URL fica ruim em
cenários de muitos parâmetros na query string.
GET https://api.minhagastronomia/vinhos
Accept: application/vnd.gastronomia.v2+json
GET https://api.minhagastronomia.com/vinhos
api-version: 2
Note que nesse caso eu criei um novo header chamado “api-version” para que o
consumidor especifique a versão desejada na requisição.
O ponto positivo é que sua URL permanece clean e intacta, o ponto negativo é que não
é dev-friendly, pois a requisição tem que ser feita com muito mais cuidado.
22
Boas práticas
Versionamento é uma das maiores discussões ao redor da comunidade de APIs, pois a
própria especificação não cria uma boa experiência ao developer.
Por isso, minha recomendação é seguir o melhor das duas abordagens com foco em
proporcionar uma ótima experiência ao desenvolvedor que irá consumir a API, ou seja,
o versionamento não pode influenciar negativamente na jornada do consumidor.
Por outro lado, o criador da API também deve conseguir ter um mecanismo simples
que facilite a implementação de conceitos de DEVOPS, como CI (Continuous
Integrations) e CD (Continuous Delivery).
Nesse sentido, gosto da abordagem de utilização da versão na URL via PATH para ser
responsável pela estabilidade estrutural da API, e por pacotes maiores de atualização.
GET https://api.minhagastronomia.com/v1/vinhos
api-version: “2019-06-01”
23
Paginação, Ordenação e Filtros
Imaginem que agora nossa API de gastronomia já está com muitos dados, e que
ao fazer um GET de vinhos, ela me retorne mais que 10 mil registros, e isso está
demorando muito para retornar aos clients que consomem esse recurso.
Para facilitar a visão, vou listar aqui os principais motivos de você implementar esses
conceitos:
Experiência do client
Como você estará fragmentando o resultado no server, a resposta da mensagem
trafegada fica bem menor, e por isso, o client consegue o resultado da requisição
muito mais rápido, criando uma experiência agradável em termos de performance ao
desenvolvedor que está consumindo a API.
24
Paginação
Cursor
A paginação baseada em cursor funciona por uma chave única e sequencial que indica
a partir de que registro os dados serão retornados.
Em outras palavras, imaginem que você quer os registros a partir do vinho de ID 156
até o ID 200. Então, você faria a seguinte requisição:
GET https://api.minhagastronomia/vinhos?since_id=156&max_id=200
Notem que foram adicionados os parâmetros since_id e max_id, que nesse caso,
são os cursores de navegação dentro da API, e que delimitam os resultados a serem
retornados pela requisição.
Page e PageSize
A paginação baseada em page e pagesize, como o próprio nome já diz, é utilizada
através dos parâmetros de número da página a ser navegada e o seu respectivo
tamanho (em número de registros).
Vamos supor que quer os dados de 10 vinhos da terceira página, então você faria a
seguinte requisição:
GET https://api.minhagastronomia/vinhos?page=3&page_size=10
25
Offset e Limit
A paginação baseada em offset e limit é utilizada a partir de um deslocamento de
registros.
Em outras palavras, você especifica no offset a partir de qual registro você quer os
dados, e no limit você especifica o limite de registros a serem retornados.
Agora vamos imaginar que você quer 30 vinhos a partir do décimo registro, então você
faria a seguinte requisição:
GET https://api.minhagastronomia/vinhos?offset=10&limit=30
Filtros e Ordenação
Sobre filtros e ordenação não tem muito segredo, via parâmetros de query string você
consegue dar mais inteligência a sua API, além de dar mais capacidade ao client para
trabalhar com os dados sob demanda. Exemplo prático de filtro:
GET https://api.minhagastronomia/vinhos?pais=Brasil&estado=RS&de_
preco=100&ate_preco=200&status=disponivel
GET https://api.minhagastronomia/vinhos/disponiveis?
estado=RS&de_preco=100&ate_preco=200
26
Exemplo prático de ordenação:
GET https://api.minhagastronomia/vinhos?sort=pais:asc,estado
No exemplo acima, notem que eu inclui o parâmetro “sort” que contém o campo chave
da ordenação seguido por “:” para especificar se quero os dados em ordem ascendente
ou descendente, além da vírgula de separação dos campos.
Esse não é o único padrão dessa técnica, mas particularmente acho bem clean.
Boas práticas
A navegação por page e page size, requer recálculos das páginas em cenários de
mudança do page size, e isso acaba criando uma complexidade adicional e que
prejudica a experiência do desenvolvedor.
E se você for utilizar page e page size, por favor, especifique na sua documentação em
que página é iniciada a busca dos dados, pois já vi inúmeros casos de que a página 0 e 1,
retornam os mesmos registros!
27
Outro ponto importante também é não colocar parâmetros como obrigatórios casos
que não fazem sentido, por exemplo:
GET https://api.minhagastronomia/vinhos?pais=Brasil&estado=RS&de_
preco=100&ate_preco=200&status=disponivel
Imaginem que nessa requisição acima, o parâmetro de status fosse obrigatório, isso
não faria o menor sentido, pois, caso eu quisesse todos os vinhos independente do
status, eu teria que concatenar diversas requisições.
E por último, pense sempre em casos de uso da sua API quando for criar um novo
recurso, isso irá facilitar bastante na definição dos parâmetros e dos metadados.
28
Políticas em APIs RESTful
Esse é um assunto muito importante, pois normalmente uma API é desenvolvida para
ser uma interface pública de comunicação.
Como tudo na vida, se você disponibiliza algo liberado e sem regras, você pode ter
problemas.
Nesse sentido, se você conhecer bem sobre políticas de APIs, você conseguirá manter
tudo sob controle.
Além disso, existem outros benefícios de utilização dessa técnica, como maior
segurança e escalabilidade, pois você praticamente obriga o consumidor da sua API
a utilizar sua infraestrutura de forma mais consciente. Agora vamos entender mais
sobre isso na prática:
Em linhas teóricas, rate limit e throttling são complementares mas não tem as mesmas
funções.
29
Ambos podem ser configurados de forma granular, como por exemplo: por client, por
recurso, pela API, etc.
Caso o client faça uma requisição às 20:00, nossa API deveria se comportar da
seguinte forma:
Notem que ao chegar na sétima requisição, então a API não deverá permitir mais
requisições, e retornará o HTTP status code 429 - Too Many Requests.
API Quota
Em uma visão comercial de uso da API e com um período maior de renovação das cotas
de consumo, o termo API quota é bastante utilizado.
Notem que agora estou falando de um período mensal, e não mais minutos, ou horas,
que são períodos bem mais curtos.
30
Isso normalmente é utilizado quando o consumo da API é cobrado do client, pois
facilita a visualização do consumo na fatura (É quase como uma conta de celular.)
É importante ressaltar que as API quotas podem ser combinadas com o rate-limiting,
pois você pode limitar um client de consumir 5 mil requisições em um mês via API
Quota e ao mesmo tempo limitar um client de consumir 100 requisições por minuto
via Rate Limit, notem que os períodos não batem, pois o client não irá sempre consumir
100 requisições por minuto, então no fundo as técnicas não tem dependência direta.
31
Performance em APIs RESTful
Eu sempre comento por aí que desenhar a arquitetura de uma solução é uma arte.
E por mais que existam diversos patterns e técnicas, um projeto real tem inúmeras
variáveis, por isso, não é algo tão trivial quanto parece.
Nesse capítulo, vou esclarecer um pouco mais esse tema e ensinar como ter mais
performance em APIs RESTful.
O que é cache
Cache é uma estrutura computacional de armazenamento focada em manter cópias de
dados que são acessados com frequência.
O propósito do cache é acelerar a busca de dados que são muito utilizados e poupar a
utilização de recursos de um servidor. Com o cache, você tem os seguintes benefícios
na sua API:
Esses caches podem ser implementados através do browser, servidor proxy, gateway,
CDN, proxy reverso, ou load balancer de servidores WEB.
32
Cache no client
Se você leu até aqui, já sabe que uma API RESTful utiliza o protocolo HTTP para
comunicação entre cliente e servidor.
Dado esse cenário, o protocolo HTTP tem suas especificações (RFC) para utilização de
políticas de cache.
Todas elas são especificadas no header de uma requisição, agora vamos entender quais
são as políticas disponíveis. Aqui não vou citar a especificação do HTTP 1.0, porque os
métodos dessa versão estão depreciados.
Controle de cache
O header cache-control é recomendado para declaração de mecanismos de cache. As
principais diretivas disponíveis são:
No caching
Com a diretiva “no-store”, você estará especificando para que nem na requisição do
client, e nem na resposta do server seja utilizado cache. Em outras palavras, toda vez
que uma requisição for feita, o client fará o download da resposta do server.
Cache-Control: no-store
33
Expiration
A diretiva “max-age” é utilizada para setar o máximo de tempo (segundos) em que os
dados ficarão disponíveis em cache.
Essa diretiva diz implicitamente ao browser que ele pode armazenar a página em
cache, mas deve validar novamente com o servidor se a duração máxima for excedida.
Cache-Control: max-age=30
Validation
A diretiva “must-revalidate” obriga que o browser sempre revalide os dados com o
servidor antes de utilizar o cache, ou seja, nesse caso, o cache continua sendo utilizado
normalmente, entretando ele é revalidado a cada nova requisição.
Cache-Control: must-revalidate
Entity Tags
O header ETag é um dos mais importantes quando falamos de cache.
Ele é utilizado como resposta do server para especificar uma versão dos dados que
estão sendo retornados.
Nesse sentido, ele serve como uma chave única consistente, e possibilita um fluxo
inteligente de consumo dos dados em cache pelo client.
4. O server valida se aquele dado foi modificado, e caso ele não tenha sofrido
alterações, o server retorna o HTTP status code 304 — Not modified.
34
Cache no Server
No tópico anterior entendemos como são as instruções de cache a nível de client HTTP.
Então vamos entender quais são as principais técnicas disponíveis para quem vai
desenvolver uma API RESTful.
Load Balancing
Em cenários que sua API tem uma quantidade de requisições alta para apenas um
servidor, então você utiliza essa técnica para distribuir a capacidade de processamento
das requisições e tráfego de rede, com isso você torna sua API mais escalável e segura,
pois se um servidor falhar, você terá outros funcionando. O load balancing por sua
vez não tem uma funcionalidade default de cache, mas tem uma funcionalidade de
armazenamento de sessão.
Web server 1
GET /vinhos/123
Web server 2
Client
Web server 3
Como sabemos, o HTTP é stateless, ou seja, não armazena a sessão, porém quando é
utilizado um load balancing, para fazer a melhor distribuição das requisições, ele faz o
armazenamento da sessão do client, e por isso você pode utilizar como uma espécie de
cache.
35
Proxy Reverso
Um proxy reverso é basicamente uma interface pública da sua API, ou seja, ele
funciona como um intermediador de todas as requisições externas.
Web server 1
GET /vinhos/123
Web server 2
Client HTTP/200 OK Proxy
Web server 3
Gateway
Pense num proxy reverso com mais funcionalidades, pensou? Então esse é o
Gateway… Ele tem todas as funcionalidades de um proxy reverso, e muito mais.
Na prática além de ser a interface de acesso a sua API para a internet, o gateway
normalmente traz funcionalidades interessantes como: roteamento, monitoramento,
autenticação e autorização, transformação de dados, segurança avançada por
políticas, etc.
36
Nesse caso, ao invés de utilizar o web server, você utiliza plataformas de API
Management, como LinkApi, por exemplo. Veja na imagem a seguir:
API Gateway
GET /vinhos/123
Security
Um API Gateway já traz toda inteligência de cache embarcada, pois utiliza a própria
funcionalidade de cache do proxy reverso, e para questões mais avançadas ele utiliza
outros mecanismos de armazenamento de dados de alta performance, como Redis e
Memcached, por exemplo.
CDN
CDN é um conceito muito difundido no mundo Web, e quer dizer Content Delivery
Network — Rede de Distribuição de Conteúdo. Ela funciona como uma rede de
servidores que mantem réplicas de conteúdos Web e fazem uma distribuição mais
otimizada desses conteúdos.
Essa técnica pode ser utilizada para Web no geral, ou seja, não é restrita apenas para o
mundo de APIs. Os principais players de CDN são: Akamai e CloudFront.
37
Compressão de dados [Bônus]
O padrão REST permite diversos formatos de dados como XML, JSON, HTML, etc. Em
todos eles, é possível comprimir a mensagem, fazendo com que o servidor trafegue
menos dados, e gerando uma melhoria de performance na API. Para isso, é necessário
a utilização de alguns headers na requisição:
Accept-Encoding
Como client, o header da requisição ficaria da seguinte forma (sendo que o Gzip
é o formato padrão de compressão de dados):
Accept-Encoding: gzip,compress
Content-Encoding
No cenário em que o servidor tem a implementação da compressão de dados, além
de obviamente compactar os dados para retornar ao client de forma otimizada, ele
também retorna um indicativo desse processo através do header Content-Encoding,
ou seja, o client deve receber o seguinte resultado:
200 OK
Content-Type: text/html
Content-Encoding:gzip
Se for um formato conhecido, porém o servidor não tiver implementado, então ele
deverá retornar o status code 415 — Unsupported Media Type.
38
O nível supremo em APIs RESTful
39
Nível 1
Esse nível é tão básico que nem vou perder muito tempo nele.
Nesse primeiro nível você já está sabendo utilizar os recursos de forma individual, ou
seja, cada URL com uma responsabilidade e um objeto específico, porém você ainda
não sabe como utilizar os métodos HTTP e outras funções mais avançadas.
Nível 2
No nível 2 você já entende um pouco mais sobre REST, e por isso já se preocupa em
utilizar a especificação do HTTP através de métodos, headers, códigos de status, e
paginação.
Não recomendado
mediumapi.com/getaccounts
Recomendado
GET mediumapi.com/accounts
POST mediumapi.com/accounts
PUT mediumapi.com/accounts
DELETE mediumapi.com/accounts
40
Use Content-Type no header
Eu sei que ainda é muito comum utilizar o formato dos dados na URL via extensão, mas
existe um header específico para isso.
Não recomendado
GET /entities/123/payable_accounts.json
Recomendado
GET /entities/123/payable_accounts
Accept: application/json
Content-Type: application/json
Não recomendado
/accounts/00928376/transaction/8738903
/zip/6500/city/dublin/state/ca
Recomendado
/accounts/00928376/action/8738903
?zip=6500&city=dublin&state=ca
41
Utilize status code HTTP pra tudo
Essa eu não precisava nem falar, mas vamos lá… Se você quer proporcionar uma
comunicação padronizada e que utiliza as melhores práticas, então você precisa
utilizar os códigos de status padrões do protocolo HTTP.
Paginação e filtros
Para proporcionar uma boa experiência em termos de performance e busca de
dados, então você precisa tomar os seguintes cuidados:
Notem que a API acima é para buscar tickets, mas como parâmetro obrigatório ela
solicita o Requester, o que anula diretamente o objetivo principal do endpoint e
prejudica a experiência do consumidor da API.
42
Nível 3
No nível 3, você já está implementando questões mais avançadas como
versionamento, rastreabilidade e HATEOAS.
Outra característica desse nível é que você já tem uma certa preocupação com
a experiência do developer que utiliza sua API, e por isso, faz o uso de padrões
universais, e cria objetos pensando em legibilidade.
43
Versionamento
Existem diversas técnicas de versionamento de APIs, e para exemplificar o assunto,
abaixo quero comparar dois modelos distintos de versionamento:
Não recomendado
API Salesforce
yourInstance.salesforce.com/services/data/
[{ "version" : "20.0", "label" : "Winter '11",
"url" : "/services/data/v20.0"},
{"version" : "21.0", "label" : "Spring '11",
"url" : "/services/data/v21.0" }]
Recomendado
API Stripe
api.stripe.com/v1/charges
-u sk_test_4eC39HqLyjWDarjtT1zdp7dc:
-H "Stripe-Version: 2019-03-14"
No primeiro exemplo (API Salesforce), podemos notar que tanto a versão quanto a
subversão ficam na URL, o que dificulta bastante a navegação da API. Além disso,
podemos notar uma quantidade enorme de versões, como por exemplo versão 21. Isso
de certa forma pode até “assustar” o desenvolvedor, que ao começar a desenvolver
algo, ele nota que as mudanças são constantes.
No segundo exemplo (API Stripe), a versão fica na URL, porém a subversão fica no
header. Nesse modelo, a explorabilidade da API se mantém intacta, além de parecer
muito mais organizada, e o mais importante, proporciona uma melhor experiência ao
developer.
44
Objetos legíveis e limpos
A legibilidade, ou seja, a facilidade de leitura de um objeto pode parecer individual,
mas na prática não é. Quanto mais simples for um objeto, mais fácil é entendê-lo,
portanto não utilizem estruturas desnecessárias, como:
Não recomendado
GET /entities/:entity_id/receivable_accounts
?per_page=15&page=3
{
"receivable_account": {
"id": 1,
"entity_id": 1,
"status": 1,
"status_name": "unreceived",
"due_date": "2011-09-10",
"occurred_at": null,
"amount": "100.0",
"ticket_amount": null,
"interest_amount": null,
"discount_amount": null,
"nominal_amount": null
}
45
Gere rastros através de logs e timestamping
Tão importante quanto você permitir uma boa experiência em manipulação de dados
na sua API, é você manter uma governança. Para isso use padrões de rastreabilidade
que permita você saber quem fez a criação ou alteração de um dado, como por
exemplo:
Recomendado
{
"createdAt": 1501869101896,
"createdBy": "Thiago",
"updatedAt": 1501869101896,
"updatedBy": "Thiago"
}
Recomendado
{"createdAt": "2019-07-08T18:02:24.343Z"}
46
Use o HATEOAS — Hypermedia As The Engine Of
Application State
Um dos pontos altos do padrão REST é permitir HATEOAS. Para quem não sabe o que
é, é basicamente uma forma de retornar em um recurso links navegáveis para objetos
relacionados.
Recomendado
GET https://api.github.com/users/codemazeblog
{
"login": "CodeMazeBlog",
"id": 29179238,
"avatar_url": "https://avatars0.
githubusercontent.com/u/29179238?v=4",
"url": "https://api.github.com/users/
CodeMazeBlog",
"html_url": "https://github.com/
CodeMazeBlog",
"followers_url": "https://api.github.
com/users/CodeMazeBlog/followers",
...
}
47
Nível 4
O nível 4 é o “supra sumo” de uma API RESTful, e nesse nível, além de entregar
uma API funcionando e estável, você estará entregando uma ótima experiência pro
developer que irá consumir a sua API.
Pense que aqui você já implementou o protocolo HTTP e os padrões REST nas
suas melhores práticas, e que agora o desafio está nos detalhes que impactam na
experiência de navegação e consumo.
O fato é que quando falamos de uma API, também criamos uma experiência a partir
dela, e para que a experiência seja positiva devemos aplicar conceitos de DX ou
Developer Experience. O DX é uma junção entre princípios de desenvolvimento com
princípios de User Experience:
48
Como esse tema é extenso, vou compartilhar apenas as preocupações básicas de quem
aplica esse conceito:
49
Não recomendado
Recomendado
50
Cuidado com limites de requisições
Se a sua API não fornece algo inteligente para tratar dados em massa (bulk data) tome
bastante cuidado com o limite de requisições na sua API.
Minha recomendação é de
pelo menos um rate-limit de 40
requisições por minuto, mas
isso é um feeling do que tenho
visto como boa experiência no
mercado, e isso pode variar de
acordo com o tipo da API.
51
Tenha uma API event-driven
Esse aqui é bem útil para quem quer ter uma infraestrutura eficiente no tráfego de
dados, além de proporcionar uma boa experiência ao client.
Normalmente quando você vai consumir uma API e precisa dos dados atualizados,
você tem que ficar fazendo requisições (pooling) o tempo todo para entender
se existem dados novos ou não no servidor. Fazendo um paralelo, é quase como
perguntar para um vendedor da sua empresa o tempo todo: “Tem venda nova hoje?”
Agora pense na hipótese de que ao invés de você perguntar o tempo todo ao vendedor,
quando ele fizer uma nova venda, ele mesmo vai te notificar proativamente. Funciona
exatamente assim uma API event-driven, ela tem subscribers, ou seja, clients que
se inscrevem para serem notificados, e em caso de novos eventos, ela notifica
automaticamente todos os clients inscritos. Para ter uma API event-driven você deve
implementar webhooks na sua API.
Event
Event Subscriber 1
Front API
Event
Subscriber 2
52
Mensagens de erro dev-friendly
Existem diferentes abordagens nesse assunto, mas aqui o objetivo é retornar
mensagens friendly de erros na sua API, diminuindo a curva de diagnóstico de
problemas. Para isso, você apenas precisa customizar as respostas da sua API com
uma estrutura de erros mais organizada e detalhada. Notem que interessante essa
estrutura de response, além de retornar o status code do HTTP 404, ela te dá um
código interno do erro para você navegar na doc, e um link detalhado do erro, com as
possíveis soluções. De fato, isso impacta positivamente a experiência do developer
que irá consumir a API.
Recomendado
API Twilio
GET https://api.twilio.com/2010-04-01/
Accounts/1234/IncomingPhoneNumbers/1234
53
Conclusão
APIs do mundo.
www.linkapi.solutions