Backend¶
HTTP: o protocolo da Web¶
Definição: HTTP (HyperText Transfer Protocol)
Protocolo (desde 1990, junto com o HTML e o primeiro navegador) que define como um cliente (navegador, app, outro serviço) troca mensagens com um servidor para acessar/manipular um recurso — é a base de praticamente todo software que fala com a Web. Roda sobre uma conexão TCP já estabelecida (ver Redes) — o HTTP em si não se preocupa em endereçar máquinas ou garantir entrega de pacotes, isso já foi resolvido nas camadas de baixo.
O caminho de uma requisição¶
Antes do HTTP em si acontecer, duas coisas já resolveram "para onde" a mensagem vai:
- Resolução de DNS — o nome (
www.pudim.com.br) é traduzido para um endereço IP. - Conexão TCP — o cliente abre uma conexão TCP com o servidor, numa porta (a porta padrão do HTTP é a 80; do HTTPS, a 443 — catalogadas pela Internet Assigned Numbers Authority, IANA).
Só depois disso o HTTP em si troca sua mensagem, em texto simples, sobre a conexão já aberta:
Definição: Anatomia de uma requisição HTTP
A primeira linha contém o método (GET), a URI do recurso (/index.html) e
a versão do protocolo (HTTP/1.1). As linhas seguintes são cabeçalhos
(headers) — pares chave-valor, alguns padronizados (Host, User-Agent,
Accept), mas o protocolo aceita cabeçalhos quaisquer. Uma linha em branco encerra
os cabeçalhos; um corpo opcional (body) pode vir depois dela. A quebra de linha
segue o padrão Windows: \r\n (Carriage Return + Line Feed).
HTTP/1.1 200 OK
Date: Sun, 24 Jan 2021 18:38:10 GMT
Server: Apache/2.4.34 (Amazon)
Content-Length: 851
Content-Type: text/html; charset=UTF-8
[corpo da resposta]
A resposta espelha a estrutura da requisição: versão do protocolo, código de status e uma frase descritiva (reason phrase — só texto, a mesma informação do código em palavras), cabeçalhos, e um corpo opcional.
Métodos HTTP¶
O protocolo define um conjunto fixo de operações que um cliente pode pedir sobre um
recurso: GET, HEAD, POST, PUT, DELETE, OPTIONS, TRACE, CONNECT. Os mais
usados na prática de APIs REST são GET (ler), POST (criar), PUT (atualizar/
substituir por completo) e DELETE (remover) — ver a distinção entre eles com mais
profundidade em REST, abaixo.
Códigos de status¶
Definição: Faixas de código de status HTTP
O primeiro dígito do código categoriza o tipo de resposta:
- 1xx (Informacional) — o servidor recebeu a requisição, mas ainda não terminou de processá-la.
- 2xx (Sucesso) — a requisição foi recebida, entendida e processada com sucesso.
- 3xx (Redirecionamento) — o cliente precisa executar mais alguma ação para completar a requisição (consultar um cache, tentar em outro endereço, ...).
- 4xx (Erro do cliente) — a requisição tem algum problema atribuível a quem a fez: falta de autorização, autenticação, ou parâmetro ausente/inválido.
- 5xx (Erro do servidor) — a requisição em si estava correta, mas o servidor falhou ao processá-la (erro interno, alta demanda, ...).
O código exato (200, 404, 500, ...) é a informação que mais importa numa resposta —
é o que o código de quem consome a API deve checar para decidir como reagir, não o texto
da reason phrase (que existe só para leitura humana).
URI: as partes de um endereço¶
Definição: URI e suas partes
Uma URI (Uniform Resource Identifier) se divide em:
Scheme://Authority/Path?Query#Fragment
- Scheme — o protocolo usado (
http,https,mongodb+srv, ...) — em HTTP,httpssinaliza que a conexão roda sobre uma camada de segurança adicional (ver TLS) por cima do HTTP puro. - Authority — quem será acessado: usuário, senha (opcionais), servidor e porta
(
usuario:senha@servidor:porta) — quando a porta não é informada, assume-se a porta padrão daquele protocolo. - Path — qual recurso, dentro daquele servidor, está sendo endereçado.
- Query — parâmetros adicionais, no formato
chave=valorseparados por&. - Fragment — referência a uma posição específica dentro do próprio recurso (ex.: uma âncora de página).
URIs não são exclusivas do HTTP — o mesmo formato geral aparece em connection strings de banco de dados, repositórios Git, e outros protocolos.
Métodos HTTP: idempotência¶
Definição: Idempotência
Propriedade (herdada da matemática) de uma operação cujo resultado é o mesmo, não importa quantas vezes ela seja aplicada. Uma requisição idempotente pode ser reenviada (por causa de uma falha de rede, um retry automático, ...) com segurança — o efeito final continua idêntico ao de enviá-la uma única vez.
| Método | O que faz | Idempotente? |
|---|---|---|
GET |
Recupera um recurso | Sim |
HEAD |
Como GET, mas sem corpo na resposta (só cabeçalhos) |
Sim |
PUT |
Sobrescreve um recurso por completo (ou cria, se aplicável) | Sim |
DELETE |
Remove um recurso | Sim |
OPTIONS |
Retorna quais métodos são válidos para aquele recurso | Sim |
TRACE |
Ecoa a requisição recebida, para diagnóstico | Sim |
POST |
Cria um novo recurso a partir dos dados enviados | Não |
PATCH |
Aplica uma atualização parcial a um recurso | Não |
Definição: Por que POST não é idempotente
Um POST tipicamente cria um recurso novo a cada chamada — enviar a mesma
requisição duas vezes cria dois recursos (dois pedidos, dois registros), não um
só. É por isso que reenviar automaticamente um POST que falhou (sem confirmação de
que ele não foi processado) é arriscado, mas reenviar um GET/PUT/DELETE que
falhou é seguro.
O protocolo em si não impõe significado de negócio a cada método — quem define isso
é o estilo arquitetural escolhido para a API (REST usa os métodos de forma canônica
ligada a operações CRUD; SOAP e gRPC praticamente ignoram essa semântica, usando quase
sempre POST). Ver REST mais abaixo.
Content negotiation: decidindo o formato¶
O cliente e o servidor negociam o formato da resposta através de cabeçalhos — nenhum dos dois precisa "adivinhar" o formato do outro:
Accept: application/json, text/plain, */*
Accept-Language: en,en-US;q=0.8,pt-BR;q=0.5,pt;q=0.3
Accept-Encoding: gzip, deflate
Accept-Charset: iso-8859-5, unicode-1-1;q=0.8
Definição: Cabeçalhos Accept-* e quality factor
O cliente envia, em ordem de preferência, os formatos/idiomas/codificações que
aceita. A prioridade de cada opção é dada pelo parâmetro q (quality factor,
peso de 0 a 1) — quando omitido, o valor é 1.0 (prioridade máxima). Accept pede
um tipo de mídia (application/json, text/xml, ...); Accept-Language, um
idioma; Accept-Encoding, uma compressão (gzip, ...); Accept-Charset, uma
codificação de caracteres. O servidor decide, dentre o que consegue oferecer, qual
opção da lista melhor atende ao pedido — e informa sua escolha final na resposta,
através dos cabeçalhos equivalentes sem o prefixo Accept- (Content-Type,
Content-Language, Content-Encoding).
HTTP é stateless¶
Definição: Stateless (sem estado)
O protocolo HTTP não guarda nenhum estado da conexão entre requisições — cada requisição é completa e independente; o servidor não sabe, por conta própria, se uma requisição está relacionada a outra anterior. Isso é uma escolha de design (não uma limitação): simplifica o protocolo e facilita escalar servidores horizontalmente (qualquer servidor pode responder qualquer requisição, sem precisar "lembrar" de nada da requisição anterior). O custo é que qualquer noção de "sessão" (usuário logado, carrinho de compras, ...) precisa ser reconstruída a cada requisição, através de alguma informação enviada junto com ela — é para isso que existem cabeçalhos de autenticação, cookies e tokens.
Autenticação x autorização¶
Definição: Authentication x Authorization
Autenticação confirma quem é o usuário (ele é mesmo quem diz ser?).
Autorização confere o que esse usuário tem permissão de fazer. O cabeçalho
HTTP que carrega essa informação chama-se Authorization (não Authentication) por
convenção histórica — na prática, ele quase sempre carrega uma credencial de
autenticação, e é o servidor quem decide, a partir dela, quais autorizações aquele
usuário tem.
Definição: HTTP Basic Authentication
Método de autenticação padrão do protocolo (RFC 2617): o cabeçalho
Authorization: Basic <usuário:senha em Base64> carrega a credencial codificada
(não criptografada) em Base64 — qualquer um que intercepte a requisição consegue
decodificar a senha instantaneamente. Só é seguro quando combinado com HTTPS
(que criptografa a conexão inteira); sobre HTTP puro, é vulnerável a um ataque
man-in-the-middle (um interlocutor no meio do caminho lê e reutiliza a credencial).
Definição: Cookie
Mecanismo (RFC 6265) para o servidor guardar uma informação no cliente, que
volta automaticamente em cada requisição seguinte para o mesmo domínio — usado
tipicamente para guardar um identificador de sessão (Set-Cookie: session=...;
expires=...). Diferente de um cabeçalho de autenticação enviado manualmente pelo
cliente a cada chamada, o cookie é gerenciado pelo próprio navegador. Tem um prazo
de expiração — depois dele, o cliente precisa de um cookie novo.
Definição: JWT (JSON Web Token) e Bearer
Formato de token (RFC 7519) auto-contido, dividido em três partes separadas por .,
cada uma em Base64Url: header (algoritmo de assinatura usado), payload
(dados do usuário — id, permissões, ...) e signature (garante que o conteúdo não
foi alterado, assinado com uma chave que só o serviço de autenticação conhece).
Diferente de um cookie, o JWT não depende de o servidor "lembrar" de nada — toda
informação necessária já está no próprio token, verificável por qualquer serviço que
tenha a chave pública correspondente. É tipicamente enviado no cabeçalho
Authorization: Bearer <token> — bearer ("portador") significa que quem apresenta
o token é tratado como autorizado, sem nenhuma verificação adicional de identidade.
CORS (Cross-Origin Resource Sharing)¶
Definição: CORS (Cross-Origin Resource Sharing)
Mecanismo que permite que recursos restritos numa página web sejam solicitados a
partir de um domínio diferente daquele que serviu o recurso original. Por padrão, o
navegador bloqueia uma requisição feita por uma aplicação web hospedada em
https://example.com para uma API em https://api.outrodominio.com, a menos que o
servidor de destino explicitamente permita essa origem via configuração de CORS —
fundamental para a segurança na web, pois protege usuários de ataques como
Cross-Site Request Forgery (CSRF) e outras tentativas de um site malicioso acessar
dados de um usuário autenticado em outro domínio.
Definição: Como o navegador decide bloquear ou não
Ao fazer uma requisição para um domínio diferente, o navegador envia um cabeçalho
Origin, informando ao servidor de destino de onde a requisição está vindo. Se o
servidor aceitar, ele responde com um cabeçalho Access-Control-Allow-Origin,
especificando quais domínios podem acessar aquele recurso — se o cabeçalho não
estiver presente, ou não incluir o domínio da requisição, o navegador bloqueia o
acesso, mesmo que a requisição já tenha chegado ao servidor.
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@CrossOrigin(origins = "https://example.com") // permite requisições de 'example.com'
public class MyController {
@GetMapping("/api/resource")
public String getResource() {
return "Dados protegidos";
}
}
Definição: CORS não é uma solução de segurança completa
Por si só, o CORS não pode ser considerado uma solução completa de segurança, porque
protege apenas requisições vindas de navegadores — outros tipos de cliente HTTP,
como o Postman ou o uso de curl em linha de comando, ignoram completamente essa
restrição (ela é aplicada pelo navegador, não pelo servidor em si). Proteger de fato
o servidor exige outras medidas de segurança, como autenticação, autorização e
validação de dados.
Tratando erros¶
Um erro HTTP não é uma falha do protocolo — é uma resposta como outra qualquer, só que com um código indicando que algo deu errado. Um detalhe sutil (e polêmico) é a diferença entre uma busca sem resultado e um recurso que não existe:
Definição: 404 numa busca vazia? Não.
Uma busca que roda com sucesso, mas não encontra nenhum resultado, deve retornar
200 OK com uma lista vazia — a busca em si funcionou. 404 Not Found é para
quando a entidade específica procurada (ex.: /usuario/123) não existe. Misturar
os dois casos confunde quem consome a API: como diferenciar "busca funcionou, sem
resultado" de "algo deu errado"?
Definição: Códigos de erro específicos do domínio
O HTTP não obriga a usar só os códigos padronizados — a especificação reserva a
faixa 450-499 para códigos específicos de negócio, desde que a API documente o
que cada um significa. Uma resposta de erro também deve ter corpo explicando o
problema ({"status": 400, "message": "Parameter userId is required!"}) — retornar
só o código, sem explicação, obriga quem consome a API a adivinhar a causa.
Definição: Cuidado ao expor detalhes de implementação em erros
Uma mensagem de erro nunca deve vazar informação sensível sobre a implementação interna — um stack trace completo devolvido ao cliente, por exemplo, expõe nomes de classes, caminhos de arquivo e a estrutura interna do sistema, informação útil para quem estiver tentando explorar uma vulnerabilidade.
Cache HTTP¶
Servidores sob alta demanda processam a mesma requisição repetidamente, mesmo quando a resposta não mudou — cache evita esse reprocessamento desnecessário, guardando (em algum ponto entre cliente e servidor) uma cópia da resposta para reutilizar em requisições futuras idênticas.
graph LR
C[Cliente] --> Cache[Servidor de Cache]
Cache --> H[Servidor HTTP]
H --> DB[Base de Dados]
Definição: Cache-Control
Cabeçalho que informa por quanto tempo uma resposta pode ser reutilizada sem
verificar o servidor de novo (max-age=31536000, em segundos) — mais útil para
recursos estáticos (arquivos JS/CSS/imagens), que não mudam durante o ciclo de
vida da aplicação. Frameworks front-end modernos costumam incluir um hash do
conteúdo no próprio nome do arquivo — assim, quando o conteúdo muda, o nome muda
junto, e o cache antigo nunca fica "preso" a um conteúdo desatualizado.
Para recursos dinâmicos (que podem mudar a qualquer momento), o servidor precisa de uma forma de validar se a versão em cache ainda é válida, sem reenviar o corpo inteiro da resposta quando ela não mudou:
Definição: ETag e 304 Not Modified
ETag é um cabeçalho que representa o estado atual de uma entidade (um hash do
conteúdo, por exemplo) — o cliente guarda esse valor junto com a resposta em cache.
Numa requisição futura, o cliente reenvia esse valor no cabeçalho If-None-Match;
se o ETag do servidor ainda for o mesmo, ele responde só 304 Not Modified (sem
reenviar o corpo da mensagem, economizando banda) — se for diferente, responde
200 normalmente, com o novo conteúdo e o novo ETag.
Definição: If-Match x If-None-Match
If-None-Match é para cache: "só reprocesse se o ETag for diferente do que
eu tenho". If-Match é para controle de concorrência: "só execute essa
modificação se o ETag não tiver mudado" (evita, por exemplo, dois clientes
sobrescreverem a mudança um do outro sem perceber, cada um partindo de uma versão
antiga do dado).
Alternativa mais simples ao ETag: Last-Modified (a data da última alteração do
recurso) combinado com If-Modified-Since/If-Unmodified-Since — mesma ideia, só que
comparando datas em vez de um hash de conteúdo.
Comunicação em tempo real: WebSocket e HTTP/2¶
Definição: Half-duplex x full-duplex
Num canal half-duplex, só um lado pode iniciar uma requisição por vez — é a característica do HTTP tradicional: o servidor só responde, nunca inicia uma mensagem por conta própria. Num canal full-duplex, os dois lados podem enviar mensagens a qualquer momento, sem esperar o outro perguntar primeiro.
Definição: WebSocket
Extensão do protocolo HTTP (mesma porta e cabeçalhos, mas com schema ws:// em
vez de http://) que transforma uma conexão HTTP comum num canal full-duplex,
através dos cabeçalhos Connection: Upgrade e Upgrade: websocket — depois do
"upgrade", a conexão deixa de seguir o padrão requisição-resposta do HTTP e passa a
permitir que qualquer um dos dois lados envie mensagens a qualquer momento, até o
canal ser fechado. Resolve a limitação de um servidor HTTP tradicional não conseguir
avisar um cliente proativamente (ex.: notificações em tempo real), sem recorrer a
polling (o cliente perguntando repetidamente "mudou alguma coisa?").
Definição: HTTP/2
Revisão do protocolo focada em desempenho, não em mudar a semântica do HTTP/1.1 (os métodos, status codes e cabeçalhos continuam os mesmos). As mudanças centrais: formato binário (em vez de texto simples, mais eficiente de processar) e multiplexação de requisições numa única conexão TCP (várias requisições em paralelo, sem esperar uma terminar para começar a próxima) — incluindo Server Push, a capacidade de o servidor enviar recursos que ele já sabe que o cliente vai precisar, antes mesmo de serem pedidos.
REST, um estilo arquitetural¶
O termo REST (Representational State Transfer) surgiu na tese de doutorado de Roy Fielding (2000), analisando estilos arquiteturais para software baseado em rede — REST é um desses estilos, construído combinando propriedades de vários outros (cliente-servidor, sistemas em camadas, cache, ...) que já existiam isoladamente antes dele.
Definição: Arquitetura de software (vocabulário de Fielding)
Uma arquitetura é composta de elementos (componentes — os serviços em execução; conectores — os mecanismos que medeiam a comunicação entre eles; dados — a informação trafegada), configurações (as informações que os componentes precisam para atingir seus objetivos, ex.: o endereço IP de um servidor), propriedades (características intrínsecas de um componente, ex.: "não armazena estado entre conexões") e estilos (um conjunto de propriedades comuns a todo o sistema). Um sistema não precisa adotar todas as propriedades de um estilo — cabe a quem projeta decidir quais fazem sentido para o problema em questão.
As seis restrições do REST¶
Definição: Restrições (propriedades) do estilo REST
- Cliente-servidor — cada componente tem responsabilidade bem definida (separação entre quem pede e quem processa).
- Stateless — toda informação de estado fica no cliente; cada requisição contém tudo que é necessário para ser respondida, sem depender de uma sessão guardada no servidor (ver Stateless, acima) — permite que qualquer servidor responda qualquer cliente, essencial para escalar horizontalmente.
- Cacheável — as requisições podem ser cacheadas; criando uma interface uniforme onde o recurso é identificado pela URI, uma camada de cache pode viver entre cliente e servidor, evitando processamento desnecessário (ver Cache, acima).
- Interface uniforme — recursos são identificados pela URI e a ação desejada é identificada pelo verbo HTTP — é essa consistência que torna uma API REST previsível de usar sem ler documentação alguma vez.
- Sistema em camadas — a interface uniforme permite compor a arquitetura em várias camadas (cache, componentes de recursos estáticos, componentes de recursos dinâmicos) sem que o cliente precise saber disso.
- Código sob demanda (opcional) — o servidor pode prover, além do recurso em si, código com lógica executável pelo cliente (o que deu origem ao conceito de Single Page Application).
Como consequência direta de ser síncrono (requisição/resposta sobre o HTTP) e não suportar broadcast (uma troca sempre acontece entre exatamente dois componentes), REST não é o estilo certo para todo problema — é uma ferramenta desenhada especificamente para expor recursos gerenciáveis por uma aplicação cliente-servidor.
Construindo uma API REST¶
Em REST, a URI identifica o recurso; o verbo HTTP identifica a ação sobre ele —
diferente do HTTP "original" (pensado para documentos), aqui a URI representa qualquer
abstração que a própria API decidir. Uma URI é uma sequência de tokens separados por /,
onde cada token tipicamente alterna entre tipo do recurso e identificador —
permitindo compor caminhos aninhados que expressam relação entre recursos:
GET /tiquete → lista todos os tiquetes
POST /tiquete → cria um novo tiquete
GET /tiquete/:id → um tiquete específico
GET /tiquete/autor/:autorId → tiquetes criados por um autor
PUT /sprint/:id/tiquete/:id → associa um tiquete a um sprint
Definição: Não existe um mapeamento oficial verbo → ação
O protocolo não define qual verbo usar para qual operação — a interpretação mais
comum (e a que a maioria dos guidelines de mercado recomenda) é GET (ler,
idempotente), POST (criar, não idempotente), PUT (criar ou atualizar por
completo, idempotente), PATCH (atualizar parcialmente) e DELETE (remover,
idempotente) — mas nada impede uma API definir sua própria convenção. O que importa
é escolher uma convenção e documentá-la, mantendo consistência em toda a API —
inconsistência entre endpoints da mesma API é pior do que qualquer escolha
específica de convenção.
Definição: REST x RESTful
REST é o estilo arquitetural em si (o conjunto de restrições descrito acima). RESTful descreve uma implementação concreta que segue esse estilo — uma API RESTful é uma API que segue os princípios REST, do mesmo jeito que "orientado a objetos" descreve um código que segue os princípios de OO.
Definição: \"API REST é uma API HTTP que devolve JSON\" — mito
Uma afirmação parcialmente correta e parcialmente errada. É verdade que toda API REST é uma API HTTP — mas nem toda API HTTP é REST (SOAP também roda sobre HTTP, e não é REST). E é comum uma API REST devolver JSON — mas o estilo REST não define formato de corpo nenhum (a tese de Fielding nem menciona JSON): ele se preocupa só com o design da URI e o uso dos verbos HTTP. Uma API pode seguir REST à risca e devolver XML, texto puro, ou qualquer outro formato.
O modelo de maturidade de Richardson (RMM)¶
Nem toda API que se diz "REST" segue os princípios do estilo na mesma medida — o Richardson Maturity Model (RMM), proposto por Leonard Richardson e popularizado por Martin Fowler, organiza essa aderência em quatro níveis progressivos, apelidados de "The Glory of REST" (a glória do REST) para o nível mais alto.
flowchart BT
N0["Nível 0: The Swamp of POX<br/>(um único método HTTP, tudo no payload)"]
N1["Nível 1: Resources<br/>(URIs por recurso, ainda um único verbo)"]
N2["Nível 2: HTTP Verbs<br/>(verbos e códigos de status corretos)"]
N3["Nível 3: Hypermedia Controls<br/>(HATEOAS)"]
N0 --> N1 --> N2 --> N3
Definição: Nível 0 — The Swamp of POX
O nível mais baixo de maturidade: a API usa HTTP só como transporte, normalmente com
um único método (POST) e uma única URI para expor todas as operações — a
diferenciação entre "o que fazer" fica inteiramente no corpo (payload) da
requisição. Web services SOAP tipicamente se encaixam aqui — conhecido como The
Swamp of POX (Plain Old XML), um apelido irônico para essa "lama" de XML sem
padronização de verbos ou URIs.
Definição: Nível 1 — Resources
Introduz o conceito de recursos: em vez de uma única URI para tudo, cada
contexto de negócio ganha sua própria URI (/vendas, /estoque, /inventario) —
mas ainda tipicamente usando um único verbo HTTP (POST) para todas as operações
daquele recurso, variando o payload para indicar a ação.
Definição: Nível 2 — HTTP Verbs
O mínimo esperado de uma API bem modelada hoje, e onde a maioria das aplicações se
encaixa: os verbos HTTP (GET, POST, PUT, PATCH, DELETE) e os códigos de
status passam a carregar o significado da operação — eliminando a necessidade de
variar o payload só para indicar "o que fazer". A responsabilidade sai do corpo da
requisição e vai para o protocolo HTTP em si.
| Verbo | Quando utilizar | Código de sucesso |
|---|---|---|
POST |
Criação de recursos | 201 Created |
PUT |
Atualização de recursos | 204 No Content |
PATCH |
Atualização parcial de recursos | 204 No Content |
DELETE |
Deleção de recursos | 204 No Content |
GET |
Obtenção de recursos | 200 OK |
Definição: Nível 3 — Hypermedia Controls (HATEOAS)
O nível mais alto — "a glória do REST" propriamente dita. Introduz o HATEOAS (Hypermedia As The Engine Of Application State): a resposta da API passa a incluir links que indicam ao cliente quais ações/recursos ele pode acessar a seguir, a partir do estado atual — a mesma ideia de navegar por um site, onde cada página tem links claros para as próximas ações possíveis, tornando a API autoexplicativa e descobrível sem depender inteiramente de documentação externa.
Definição: HATEOAS é opcional, mesmo em APIs maduras
Uma API só é chamada de RESTful quando atinge o Nível 2 ou 3 de maturidade. O Nível 3 (HATEOAS), porém, deve ser adotado com ponderação: a complexidade adicional de manter e navegar por links nem sempre se justifica — a grande maioria das APIs consideradas bem modeladas no mercado hoje para no Nível 2, sem que isso seja considerado um problema.
Outros estilos: RPC, gRPC, SOAP e GraphQL¶
REST não é o único estilo usado sobre HTTP — vale conhecer os concorrentes mais comuns e, principalmente, quando cada um resolve melhor um problema que o REST resolveria com mais atrito.
Definição: RPC (Remote Procedure Call)
Modelo de programação (desde 1976) baseado na premissa de que cliente e servidor podem se comunicar como se estivessem no mesmo processo — quem desenvolve só precisa conhecer uma interface de código, chamando um método remoto como chamaria um método local qualquer. O protocolo de comunicação e a serialização ficam escondidos atrás de um framework, que gera esse código de acesso automaticamente.
Definição: A falácia da chamada local (RPC)
Por mais que uma chamada RPC pareça uma chamada de método local, ela nunca é — toda chamada remota atravessa uma rede de verdade, com todos os problemas que isso traz (conexão instável, servidor fora do ar, resposta que nunca chega mesmo que tenha sido processada). Tratar uma chamada RPC como garantidamente confiável, só porque a sintaxe parece local, é a origem de bugs difíceis de rastrear em sistemas distribuídos.
Definição: gRPC
Framework RPC do Google (2015) — usa Protocol Buffers (protobuf, uma linguagem/formato de serialização binária do próprio Google) para definir a interface e serializar os dados, sobre HTTP/2. Diferente do REST, o gRPC não é orientado a recursos, é orientado a serviços: em vez de desenhar uma URI, se define uma mensagem (os campos de dados) e um serviço (os métodos remotos disponíveis, cada um recebendo e devolvendo uma mensagem).
message Tiquete {
int32 id = 1;
string titulo = 2;
}
service TiqueteService {
rpc getTiquetePorUsuario(EncontraTiquetePorUsuario) returns (Tiquete) {}
}
Cada campo protobuf tem um número identificador (= 1, = 2, ...) usado na
serialização binária em vez do nome do campo — permite que uma mensagem evolua
(adicionando ou removendo campos) sem quebrar a comunicação entre versões
diferentes, desde que os números já existentes nunca sejam reaproveitados. O
formato binário e o HTTP/2 tornam o gRPC mais rápido que REST/SOAP em cenários de
alta performance — o custo é perder a legibilidade humana direta do JSON/XML, e a
curva de aprendizado de uma ferramenta nova.
Definição: SOAP (Simple Object Access Protocol)
Estilo mais antigo que gRPC, também baseado em RPC — mensagens (requisição e
resposta) trafegam como XML, sempre encapsuladas num Envelope contendo um Body.
Por se limitar ao XML (sem o ganho de performance de um formato binário como o
protobuf), perdeu espaço para gRPC e REST ao longo do tempo, mas continua em uso em
sistemas legados e cenários corporativos que já investiram nele (ex.: um Enterprise
Service Bus, ferramenta para agregar e disponibilizar vários serviços SOAP).
Definição: GraphQL
Estilo criado para resolver um problema específico: clientes diferentes (web, mobile, ...) frequentemente precisam de subconjuntos diferentes dos mesmos dados. Com REST/SOAP, atender isso exigiria endpoints diferentes (ou superdimensionados) para cada necessidade de cliente. Em GraphQL, o cliente define, a cada chamada, exatamente quais campos quer receber — tudo através de um único endpoint, evitando tanto o over-fetching (receber campos que não serão usados) quanto o under-fetching (precisar de uma segunda chamada para completar o que faltou). Não concorre diretamente com REST: pode inclusive ser implementado dentro de uma mesma API REST, compartilhando a mesma base de código.
Quando usar cada um¶
Nenhum estilo substitui os outros por completo — a escolha depende do problema: REST
é a opção mais geral e madura para expor recursos a uma aplicação cliente-servidor
comum; gRPC brilha em comunicação interna entre serviços (microsserviços
conversando entre si), onde performance importa mais que legibilidade humana e os dois
lados podem compartilhar os arquivos .proto gerados; SOAP ainda aparece em
sistemas corporativos legados, mas raramente é escolhido para projetos novos; GraphQL
vale a pena quando existem clientes com necessidades de dados muito diferentes entre si
consumindo a mesma API.