- No artigo original sobre REST de Roy Fielding, o uso de métodos HTTP ou de APIs centradas em CRUD não é definido de forma explícita, e REST originalmente enfatiza o projeto de sistemas baseados em hipermídia (HATEOAS)
- Muitas coisas que as pessoas chamam de APIs RESTful não implementam restrições importantes do REST (especialmente o uso de hipermídia)
- Recurso não se limita a estruturas de dados ou entidades simples, mas inclui qualquer conceito identificável por URI
- Segundo as 6 regras de Fielding, navegação centrada em hipermídia, independência de protocolo e foco em tipos de mídia são elementos centrais
- Na prática, por causa da conveniência de ferramentas de documentação como OpenAPI, a maioria das APIs tende a ser projetada mais próxima do estilo RPC do que de um RESTful genuíno
Por que a maioria das APIs RESTful não é realmente RESTful
- O artigo clássico (2000) de Roy Fielding apresenta REST (Representational State Transfer) como um estilo ideal de projeto para software em rede e o descreve como o princípio que sustenta o sucesso da web
- O artigo não define uso de métodos HTTP nem design centrado em CRUD como exigência, e enfatiza que, ao buscar REST, o essencial são transição de estado baseada em hipermídia (HATEOAS), interface uniforme e interação centrada em recursos
Hipermídia (HATEOAS) e os mal-entendidos sobre REST
- Fielding aponta claramente que uma API sem hipermídia não é RESTful
- "Se o mecanismo não é conduzido por hipertexto, então não é RESTful"
- HATEOAS é uma abordagem em que o cliente descobre dinamicamente as ações seguindo links embutidos na resposta do servidor
- Simplesmente usar uma interface HTTP/CRUD não é o mesmo que a essência do REST
Equívocos comuns sobre REST
- A ideia de que REST é CRUD (quando na verdade é um conceito mais amplo)
- O mal-entendido de que recurso é uma entidade (a estrutura de dados do servidor)
- A afirmação de que uma API RESTful não pode usar verbos (Verb)
- Essas coisas são apenas decisões de design e não têm relação direta com a essência do REST
O significado real da navegação orientada por hipermídia (HATEOAS)
- O objetivo do HATEOAS é minimizar o acoplamento entre cliente e servidor
- Quando a estrutura de URI do servidor muda, isso reduz o custo de redistribuir clientes e aumenta a escalabilidade e a capacidade de evolução
- O cliente descobre como agir seguindo hiperlinks na resposta, sem depender de documentação nem de conhecimento prévio
{
"orderId": 123,
"\_links": {
"self": { "href": "/orders/123" },
"cancel": { "href": "/orders/123/cancel", "method": "POST" }
}
}
- Como no exemplo acima, é preciso que a resposta inclua ações (links) para ficar mais próxima de um RESTful de verdade
O que é um recurso em REST?
- Recurso é qualquer coisa identificável por URI
- Inclui documentos, imagens, objetos físicos, serviços, conceitos abstratos etc.
- No servidor, não existe necessariamente um “recurso” concreto, mas sim uma estrutura de mapeamento abstrata acessível por URI
- A própria RFC 3986 não limita o escopo de recurso
- Pode incluir documentos eletrônicos, imagens, fontes de informação, serviços e até pessoas, entidades jurídicas ou livros
As 6 regras de uma API RESTful definidas por Fielding
- 1. Não depender de um único protocolo
- A identificação baseada em URI deve poder ser usada em qualquer protocolo
- 2. Não alterar arbitrariamente o padrão do protocolo (ex.: HTTP)
- É possível complementar lacunas do padrão, mas não adicionar ou mudar regras de forma arbitrária
- 3. Focar em definir tipos de mídia (formatos), e não a estrutura de URI
- Concentre-se no formato dos dados e na definição de links, sem desperdiçar energia com especificação/documentação de caminhos
- 4. Não fazer hardcode de nomes fixos de URI ou hierarquias
- Em vez de fazer o cliente adivinhar ou fixar a estrutura de namespace do servidor, conduza a navegação por links
- 5. Não expor tipos de recurso (classificação interna)
- Tipos internos de objeto não têm utilidade para o cliente; exponha apenas tipos de mídia e links padronizados
- 6. Só é necessário um bookmark (URI inicial); o resto deve ser navegado pelos links da resposta
- O cliente conhece apenas tipos de mídia padronizados, e a transição de estado deve sempre ocorrer com base nos hiperlinks presentes na resposta do servidor
Interpretação das regras e aplicação prática
- Para se aproximar de um RESTful verdadeiro, é preciso minimizar o acoplamento a protocolo, URI e tipos
- A API deve se concentrar no formato de dados e links (tipos de mídia), e o cliente não precisa conhecer previamente a estrutura de URI nem convenções de nomenclatura
- Em vez de especificar caminhos como
/users/123/activate, deve-se orientar a ação por meio de um link "activate" na resposta
Por que APIs realmente RESTful são raras
- A conveniência de ferramentas como OpenAPI e Swagger costuma ser priorizada no desenvolvimento
- Elas oferecem benefícios concretos, como documentação automática, geração de código e validação
- Em ambientes de SPA onde cliente e servidor são desenvolvidos pela mesma equipe, a necessidade de resolver o problema de acoplamento por URI é menor, então as vantagens do HATEOAS ficam menos evidentes
- O custo inicial de aprender os princípios de REST e a complexidade de fazer parsing de links dinâmicos também são barreiras práticas relevantes
Conclusão
- Segundo as regras de Fielding, uma API RESTful de verdade exige navegação dinâmica baseada em hipermídia (HATEOAS)
- REST não é simplesmente implementar CRUD sobre HTTP, mas uma arquitetura focada em baixo acoplamento, capacidade de evolução e transição dinâmica de estado, como os próprios princípios da web
- Na prática, a utilidade e o contexto da equipe podem ser mais importantes
- Para uma API pública voltada a desenvolvedores externos, adotar HATEOAS pode ser recomendável; para uma API apenas interna, um estilo RPC simples também pode ser prático
- O essencial é projetar a API para que seja fácil de aprender e difícil de usar incorretamente, e ela não precisa necessariamente ser RESTful
1 comentários
Comentários no Hacker News
Eu me identifico com o purismo excessivo que aparece aqui e também achei a dissertação do Fielding interessante, mas sinto que essa guerra já acabou. No momento em que ouço o termo API REST, quase sempre posso assumir o seguinte
que chama
e no backend existe uma função como
com anotações descrevendo o mapeamento da URL. No fim, isso é RPC. Só que, em vez de um sistema complexo e pouco intuitivo, ele sobrevive numa forma mais manual e sob controle do desenvolvedor. Em 80% da maioria dos problemas, a causa é as pessoas não usarem o formato de data ISO 8601
Não entendo muito bem por que alguém trata isso como uma "batalha" e se importa tanto. O conceito de REST é útil, mas a parte de HATEOAS quase não tem utilidade prática e só cria problemas. Se você olhar o Richardson maturity model, o topo do REST inclui elementos como HATEOAS. Também não entendo a lógica de dizer que, sem HATEOAS, não dá para chamar de REST, porque isso não é um diferencial tão grande assim. Se HATEOAS é, na prática, quase sem sentido, então parece não fazer sentido se apegar a essa classificação purista. Richardson maturity model
Na parte sobre "equipes discutirem demais códigos de status e muitas vezes usá-los de forma diferente da especificação HTTP", vale reforçar que 401 Unauthorized deve ser usado quando falta autenticação, e 403 Forbidden quando falta permissão
Eu costumo perguntar às pessoas se elas realmente pretendem usar o significado de REST definido no paper. Em geral sou do tipo que não tolera o abuso de termos sem sentido. No fim, concluo com "ah, então você quer dizer apenas uma web API" e sigo em frente. O importante é que cada uma dessas APIs tem suas próprias esquisitices que precisam ser entendidas
A nuance que realmente importa para mim é que os "links de hipermídia" parecem algo "genérico" por meio de vários tipos de link (incluídos em headers HTTP ou no resultado retornado), mas, na prática, o REST de hoje funciona do mesmo jeito. Por exemplo, se por erro de design deveria ser "enable" em vez de "activate", no fim você vai ter de mudar de /api/v1/account/ID/activate para /api/v2/account/ID/enable. Ou seja, é preciso hardcodear em algum lugar o significado de todas as ações da API (e ainda faltam metadados adicionais como ícones, tradução da descrição das ações etc.). Essa suposta "generalidade" é, na verdade, uma ilusão
Quando fui desenvolver uma HTTP API pela primeira vez, há 13 anos, li a dissertação do Fielding do começo ao fim para entender o que era REST de verdade, e também li RESTful Web Services Cookbook. Então construí uma API REST fugindo das convenções do Django. Foi uma espécie de abordagem de "cargo cult", porque eu não entendia bem que vantagens o REST real traria para o nosso serviço. Só depois de mais alguns anos criando diferentes HTTP APIs percebi que, no contexto prático, o REST em seu sentido mais ortodoxo não traz benefícios. A visão de "autodescoberta" e "compatibilidade com clientes genéricos" é quase impossível de realizar e, especificamente, a dissertação do Fielding nem sequer orienta isso perfeitamente. Para criar uma API realmente autodetectável, seriam necessárias regras concretas como "protocolo de descoberta de endpoints", "descrição de operações", "mensagens de ajuda" etc. E, no fim, você precisa criar um cliente dedicado que entenda essas regras, e aí a vantagem de um cliente generalizado desaparece. Ou seja, na prática, não houve alternativa além de criar código específico para cada servidor, seja API, código JS, CLI etc. E uma boa UX entra em conflito com o ideal do REST, porque é preciso escrever código específico do app no frontend para alcançar uma UX realmente boa. Até dá para padronizar elementos de UI, mas, na prática, é muito mais útil construir interfaces flexíveis com linguagens como JavaScript
Concordo com os limites do conceito de API autodetectável. Um cliente REST de verdade é praticamente impossível de implementar. Ele precisaria conhecer o comportamento de todas as URLs e, se surgir uma nova ação (ex.: /cansofspam/123/frobnicate), o cliente não saberá lidar com isso de forma clara. No fim, seria necessário atualizar o cliente, ignorar a ação ou no máximo adicionar um botão muito simples (ex.: Frobnicate). Por isso, não existem servidores ou clientes REST no sentido pleno do termo. Na prática, o que funciona é o cliente suportar apenas a API que ele já espera, sem discovery
APIs têm muitos aspectos, então é difícil descrevê-las adequadamente. Quem consome a API também precisa saber latência média de resposta, códigos de erro passíveis de retry, atomicidade/idempotência das ações etc. Só HATEOAS não comunica esse tipo de informação. Não é necessário implementar REST perfeito e, no fim das contas, o benefício básico do REST é só oferecer uma linguagem unificada que mapeia substantivos/verbos para métodos HTTP e URLs. Mesmo assim, ainda existe uma enorme quantidade de detalhes de design e decisões a tomar. Por exemplo, algo permitido pela especificação HTTP pode não funcionar no load balancer real, e também é preciso pensar em critérios de retry para erro 500, lógica de backoff e assim por diante
O navegador é justamente o "código genérico" e entrega a melhor UX que usamos no dia a dia. O conceito de REST também inclui a possibilidade de o servidor entregar código ao cliente (embora existam questões de segurança, o navegador e os padrões resolvem bastante disso). Link para a seção relevante na dissertação do Fielding
Na verdade, mesmo a definição enfraquecida de REST não traz muito ganho. "Você tem que usar DELETE para excluir um recurso" não é algo tão importante assim. Se usar só POST, quem vai sofrer com isso?
Eu nunca achei que "autodescoberta" fosse o objetivo, nem acho que seja algo alcançável. Em designs de cliente simples, essa expectativa já é exagerada desde o início. Especialmente porque, no TFA, a palavra ‘discoverable’ nem aparece
Esse tipo de design de API é realmente útil quando o usuário e o agent que navega em seu nome (por exemplo, o navegador) conseguem explorar a API e interagir de acordo com o media type e as informações de link presentes em cada resposta. A maioria das web APIs não é feita para esse caso de uso, e sim para web apps desenhados em torno de uma UI/UX específica. Essa é uma escolha intencional. Quem constrói o app quer controlar completamente a representação dos dados, o fluxo da UI etc. O design de API REST é necessário quando o usuário precisa ter controle mais direto sobre como os recursos da API são usados. Exemplos:
Na verdade, documentos HTML são exatamente esse caso. Há links no documento que levam a outros documentos, e o usuário pode navegar para onde quiser com base no texto do link. Se é para o usuário, chamamos de UI; se é para a aplicação, chamamos de API. HATEOAS parece estranho porque dá a impressão de querer adaptar a API diretamente ao usuário, mas nós já usufruímos disso na forma de UI
O conceito puro de REST é extremamente acadêmico. Em projetos de dados abertos ou big data, o mais importante para implementar desempenho e arquitetura reais é uma abordagem prática, não atingir ou não REST. Até os acadêmicos, no fim, precisam construir algo, então também não ficam presos apenas ao REST perfeito
Esse tipo de design de API é útil não só para páginas web, mas também para construir outros clientes. Dá para buscar recursos com GET, extrair valores por campo/caminho e criar novas URIs para manipular dados, construindo diversos apps/CLIs/UIs com padrões semelhantes. Se não for SPA, dá até para implementar diretamente em HTML; no fim, o usuário (ou user-agent) faz dereference das informações dentro da representação retornada
Fico pensando se, na era em que a IA consome APIs, esse caso de uso vai se tornar mais importante. A discoverability da API favorece muito mais a IA do que o desenvolvedor de web app. O MCP (protocolo de controle imersivo) mostra como tool discoverability pode ser poderosa. HATEOAS pode trazer vantagens potenciais significativas também para esse consumo de APIs mais bruto
A documentação da API de serviços RESTful do serviço meteorológico dos EUA é um ótimo exemplo de como uma API de informação pública bem projetada pode ser muito agradável de usar
Sobre a ideia de que "a carga cognitiva inicial para criar um cliente realmente guiado por hipermídia parecia enorme, e hardcodear URI templates (/users/{id}/orders etc.) parecia bem mais confortável", minha experiência empírica é que isso realmente é mais fácil. Os princípios puros de REST têm uma relação custo-benefício ruim na maioria das situações. É como operar um micro-ondas com um único botão que controla menu, modo de operação e tempo, em vez de simplesmente usar botões padrão de forma familiar. Até um leitor de código de motor com 2 botões que eu uso na prática tem uma operação absurdamente ruim. Essa cultura de ainda dizer que é obrigatório ler a dissertação do Fielding é bastante controversa. Se uma ideia é boa, ela deveria poder ser explicada de várias formas, de um jeito acessível e popular. Por exemplo, ninguém diz que, para entender física, é obrigatório ler o Principia de Newton
Para que padrões RESTful/HATEOAS tenham valor real quando adotados, é preciso existir um cliente que os compreenda. htmx: hypermedia clients intercoolerjs: hatoeas-is-for-humans
Designers de UI querem controlar os detalhes finos da aparência das telas. Algumas ações possíveis sobre um recurso podem aparecer como botões grandes, outras podem ficar escondidas em menus ou nem aparecer na UI. Se as ações forem renderizadas dinamicamente com base na resposta da API em cada estado, tudo passa a parecer igual. Por isso, considero APIs RESTful inadequadas para implementar UIs comuns de frontend web
Há vários erros nessa afirmação
if (resource.links['action'] !== null)do que comif (state === something). A maioria das mudanças de estado precisa de validação no servidor e, se isso ficar implementado só no servidor, a complexidade do frontend também diminui. Desenvolvi apps com HATEOAS por bastante tempo e também mantenho o HAL4J. Há complexidade, mas o design de UI em si não é a barreira de entradaNa minha experiência, desenvolver uma "API RESTful" raramente esteve diretamente ligado à UI. Se tudo o que você quer é uma UI, a própria API é desnecessária; basta usar um modelo orientado pelo servidor (como o antigo DWR)
Como HATEOAS parece quase não ser usado na prática, não entendo muito bem por que ainda se discute tanto isso. Gostaria de saber se isso realmente é usado em algum lugar e que tipo de "cliente com autoexploração" consegue funcionar sem conhecer o servidor de antemão
Vale lembrar que o ACME (protocolo do Let’s Encrypt) é baseado em HATEOAS. Isso é usado, na prática, pela maioria dos serviços HTTPS. O próprio HTTP, se usado corretamente, já é um protocolo HATEOAS. "Auto-discovery" significa conseguir navegar recursos por meio de tipos de link ou de coisas como ‘next’. Ainda assim, o cliente precisa conhecer previamente o significado de "next". LLMs também são fortes nesse tipo de exploração automática
Já usei HATEOAS em um sistema corporativo de vigilância por vídeo. Resolveu muito bem problemas de versão e permissão no nível da API. Também usamos vários RFCs junto. Mas o principal problema é que as pessoas buscam "conveniência" de um jeito que destrói o modelo e acaba gerando mais complexidade. Além disso, JSON não é, por natureza, um formato de hipertexto, então tentar enfiar HATEOAS à força em application/json parecia artificial
Você está usando HATEOAS para escrever comentários e eu estou usando HATEOAS para responder agora mesmo. O "cliente mágico de autoexploração" que faz isso é o próprio navegador web
htmx talvez seja a tentativa mais prática
Padrões como OData também quase não são usados e nem chegam a ser populares. HATEOAS tem ainda menos adoção e padronização, então acaba se expandindo menos ainda
O que sempre é fácil esquecer nessa discussão é o tipo de consumidor da API de backend. REST e HATEOAS costumam fazer mais sentido quando o consumidor é um terceiro que não possui diretamente o backend. Por exemplo, o consumidor final de páginas HTML tradicionais é o usuário do navegador. O MCP recente também surgiu porque há casos em que é preciso "descobrir e interpretar" várias APIs JSON RPC diferentes. Já quando frontend e backend estão acoplados numa relação 1:1, o benefício de REST raramente compensa o custo. É preciso criar documentação/especificações mais genéricas e, na prática, ferramentas que ignoram separation of concerns para ganhar produtividade (como trPC) muitas vezes são mais úteis. Em protótipos, a integração end-to-end é rápida
Concordo muito, e recomendo os textos HATOEAS is for humans e htmx: hypermedia clients
*HATEOAS
Sobre a afirmação de que clientes de exploração dinâmica seriam possíveis com HATEOAS e referências de schema (XSD, JSON Schema etc.), na prática recursos do JSON Schema como "additionalProperties" acabam repetindo justamente o problema original. Acho mais robusto fornecer documentação por vias "out of band". Nesse caso, e se "_links" trouxesse apenas um link para a documentação Swagger, e o cliente interpretasse as informações do self path a partir dali? Se for assim, por que "_links" precisaria existir? Se já existe um cliente capaz de processar um documento JSON tão complexo, então templates Swagger e afins oferecem muito mais densidade de informação e dinamismo. Só links CRUD não bastam para descrever a API inteira adequadamente, e é impossível cobrir tudo com JSON Schema
Se chamar simplesmente de HTTP API, todo mundo fica feliz. REST nem foi projetado para APIs desde o começo. Desde a origem, REST era voltado para sistemas de informação consumidos por pessoas, não por programas