Design de APIs REST
Recursos, nomes, códigos de status e formato de erros em uma API previsível.
Uma API é uma interface para pessoas
Quem consome uma API é um programa, mas quem escreve esse programa é uma pessoa. Ela lê a documentação, tenta um pedido, interpreta a resposta e escreve o código de integração. Uma API bem desenhada é aquela em que essa pessoa adivinha certo: depois de usar duas rotas, ela prevê como a terceira funciona.
O estilo REST, apresentado no tópico de estilos de API, organiza a API em torno de recursos manipulados pelos métodos do HTTP. Este tópico trata das decisões de desenho dentro desse estilo.
Recursos e endereços
Um recurso é qualquer coisa que a API expõe: um pedido, um cliente, os itens de um pedido. Cada recurso tem um endereço, e o endereço usa substantivos. A ação vem do método.
| Ação | Método e endereço |
|---|---|
| Listar pedidos | GET /pedidos |
| Criar um pedido | POST /pedidos |
| Ler um pedido | GET /pedidos/981 |
| Substituir um pedido | PUT /pedidos/981 |
| Alterar parte de um pedido | PATCH /pedidos/981 |
| Remover um pedido | DELETE /pedidos/981 |
| Listar os itens de um pedido | GET /pedidos/981/itens |
Os erros mais comuns de endereço:
| Em vez de | Use | Por quê |
|---|---|---|
POST /criarPedido | POST /pedidos | O método já diz a ação |
GET /pedidos/981/cancelar | POST /pedidos/981/cancelamento | GET não pode alterar nada; o tópico de HTTP explica o risco |
/Pedido, /pedido e /pedidos em rotas diferentes | Sempre /pedidos | Plural e minúsculas, sempre iguais |
/clientes/42/pedidos/981/itens/3/produto | /pedidos/981/itens e /produtos/7 | Aninhar mais de um nível deixa os endereços frágeis |
Ações que não são criar, ler, alterar ou remover
Nem tudo cabe nos quatro verbos. Cancelar um pedido, reenviar um e-mail, aprovar um reembolso. A forma mais consistente é tratar a ação como a criação de um recurso:
POST /pedidos/981/cancelamento
Content-Type: application/json
{ "motivo": "cliente desistiu" }O cancelamento passa a ser algo que existe, com data, motivo e quem o fez, e pode ser consultado depois em GET /pedidos/981/cancelamento.
Códigos de status
O tópico de HTTP apresenta os códigos de status. No desenho da API, o importante é usar cada um de forma previsível:
| Operação | Sucesso | Erros mais comuns |
|---|---|---|
GET de um recurso | 200 | 404 não existe |
GET de uma lista | 200, mesmo vazia | 400 filtro inválido |
POST que cria | 201, com o cabeçalho Location apontando o recurso criado | 400 corpo malformado, 422 dados inválidos, 409 conflito (já existe) |
POST que inicia um processo demorado | 202, com o endereço para acompanhar | |
PUT ou PATCH | 200 com o recurso, ou 204 sem corpo | 404, 409, 422 |
DELETE | 204 | 404 |
A diferença entre 400 e 422 costuma gerar debate. Uma convenção comum, usada nos exemplos deste tópico: 400 quando o pedido não pode nem ser lido (JSON malformado), 422 quando foi lido, mas os dados não passam na validação.
Erros num formato único
A parte mais negligenciada de uma API é o formato dos erros. Cada rota inventando o seu cria clientes cheios de casos especiais:
{ "erro": "pedido não encontrado" }
{ "message": "Invalid quantity", "code": 4001 }
{ "success": false, "errors": ["clienteId is required"] }O padrão Problem Details (RFC 9457) define um formato único, com o tipo de conteúdo application/problem+json:
{
"type": "https://api.loja.com.br/erros/dados-invalidos",
"title": "Os dados enviados são inválidos",
"status": 422,
"errors": {
"clienteId": "obrigatório",
"itens[0].quantidade": "inteiro maior que zero"
}
}| Campo | Para que serve |
|---|---|
type | Um identificador estável do tipo de erro; o cliente decide o que fazer por ele, e não pela mensagem |
title | Uma descrição curta, igual para todas as ocorrências desse tipo |
status | O mesmo código de status da resposta |
detail | Uma explicação desta ocorrência específica (opcional) |
instance | O endereço desta ocorrência (opcional) |
| Outros campos | Extensões, como a lista de erros por campo |
Implementamos esse formato nas cinco linguagens e executamos quatro delas. TypeScript, Python, Go e C# responderam com application/problem+json e status 422 aos dados inválidos e 400 ao JSON malformado. Os detalhes variaram: o C# coloca as mensagens em listas e acrescenta um traceId; o FastAPI usa as mensagens de validação do Pydantic, em inglês. O nível Implementação mostra a comparação completa.
Representações
| Decisão | Recomendação |
|---|---|
| Nomes de campos | Um padrão único em toda a API: camelCase ou snake_case, nunca os dois |
| Datas | ISO 8601 com fuso: 2026-03-10T14:02:00Z |
| Dinheiro | Inteiro em centavos, ou texto decimal ("199.90"), com a moeda; nunca número de ponto flutuante |
| Identificadores | Texto, mesmo que sejam numéricos no banco; o cliente não deve fazer contas com eles |
| Campos vazios | Uma regra só: omitir, ou devolver null |
| Campos internos | Nunca na resposta: senhas, hashes, colunas de controle, chaves de outros sistemas |
A última linha merece atenção. Devolver a entidade do banco diretamente, em vez de um formato desenhado para a API, expõe campos que deveriam ser internos e prende o contrato da API ao esquema do banco: uma coluna renomeada quebra os clientes.
Por que isso importa quando a IA escreve o código
Um assistente gera rotas que funcionam isoladamente, e cada uma pode sair com um estilo diferente:
- Verbos nos endereços (
/getPedido,/cancelarPedido) e ações emGET. 200para tudo, com o erro dentro do corpo.- Um formato de erro diferente em cada rota, ou o formato padrão do framework misturado com um próprio.
- A entidade do banco devolvida inteira, com campos internos.
403para recursos de outros usuários, revelando quais identificadores existem.
O próximo nível compara as decisões de desenho que mais aparecem e quando escolher cada lado.
Prompts para aprofundar
Ao abrir no ChatGPT ou no Claude, o prompt é enviado ao serviço escolhido pela URL e também fica copiado na sua área de transferência.
Design de APIs REST em cinco minutos
Explique em no máximo cinco minutos de leitura o que torna uma API REST bem desenhada, para alguém que já criou rotas mas nunca pensou na API como um contrato. Cubra só o essencial: recursos e endereços com substantivos, o método como ação, os códigos de status por operação e por que todos os erros devem seguir um único formato. Use o exemplo de uma API de pedidos. No final, me faça três perguntas curtas para eu verificar se entendi, e corrija as minhas respostas.
Desenhar os endereços de uma API
Quero aprender a desenhar os endereços e métodos de uma API REST. Aja como um mentor. Use como exemplo uma loja virtual com clientes, pedidos, itens, produtos e pagamentos. Conduza assim: 1. Peça que eu proponha os endereços e métodos para criar, listar, ler, alterar e remover pedidos. Corrija o que estiver fora do padrão. 2. Peça os endereços para os itens de um pedido e discuta até que nível aninhar. 3. Peça como eu modelaria cancelar um pedido, aprovar um reembolso e reenviar um e-mail, e explique a ideia de tratar ações como recursos. 4. Mostre cinco endereços com problemas comuns e peça que eu encontre o erro em cada um. Explique cada correção com o motivo prático.
Códigos de status e formato de erros
Quero aprender a escolher os códigos de status e a desenhar as respostas de erro de uma API. Explique: 1. O código de sucesso adequado para cada operação, inclusive 201 com Location, 202 e 204. 2. A diferença entre 400, 401, 403, 404, 409 e 422, com exemplos. 3. Por que responder 404, e não 403, para recursos de outros usuários. 4. O formato Problem Details (RFC 9457): cada campo e para que serve. 5. Como representar erros de validação de vários campos. Depois me apresente oito situações e pergunte o status e o corpo de erro que eu devolveria em cada uma. Corrija as minhas respostas.
Representações que não prendem a API ao banco
Quero entender como desenhar o formato das respostas de uma API sem prendê-lo ao banco de dados. Explique: 1. Por que devolver a entidade do banco diretamente é um problema, com exemplos de campos que vazam e de mudanças que quebram clientes. 2. Como representar datas, dinheiro, identificadores e campos vazios. 3. A escolha entre camelCase e snake_case e por que a consistência importa mais que a escolha. 4. Como evoluir a representação sem quebrar clientes: acrescentar campos é seguro, remover e renomear não. Depois me mostre a resposta de uma API com cinco problemas e peça que eu os encontre.