Padrões de Design de API JSON: Construindo REST APIs Melhores
Uma API bem projetada é um prazer de usar. Uma mal projetada cria bugs, frustração e dívida técnica. Este guia aborda padrões testados em batalha para projetar REST APIs JSON que sejam consistentes, descobríveis e fáceis de manter.
Nomenclatura de Recursos
Recursos são substantivos, não verbos. Use substantivos no plural para coleções e recursos individuais via ID:
GET /api/users # List users
POST /api/users # Create a user
GET /api/users/123 # Get user 123
PUT /api/users/123 # Update user 123
DELETE /api/users/123 # Delete user 123
Convenções de nomenclatura:
- Use letras minúsculas com hífens:
/api/blog-posts(nãoblogPostsoublog_posts) - Aninhe recursos relacionados:
/api/users/123/orders - Limite o aninhamento a 2 níveis:
/api/users/123/orders/456(não mais profundo) - Evite verbos nas URLs:
/api/users/123/activateé aceitável para ações que não se encaixam no CRUD
Envelope de Resposta
Encapsule as respostas em um envelope consistente:
{
"data": {
"id": "123",
"type": "user",
"attributes": {
"name": "Alice",
"email": "alice@example.com",
"createdAt": "2024-01-15T10:30:00Z"
}
},
"meta": {
"requestId": "req_abc123"
}
}
Para coleções:
{
"data": [
{ "id": "123", "name": "Alice" },
{ "id": "456", "name": "Bob" }
],
"meta": {
"total": 142,
"page": 1,
"perPage": 20
}
}
Valide as respostas da sua API com nosso Validador JSON para garantir que elas correspondam ao seu schema.
Paginação
Três abordagens comuns:
Baseada em Offset (Mais Simples)
GET /api/users?page=2&per_page=20
{
"data": [...],
"meta": {
"page": 2,
"perPage": 20,
"total": 142,
"totalPages": 8
}
}
Vantagem: Simples, permite pular para qualquer página. Desvantagem: Resultados inconsistentes com inserções/exclusões concorrentes.
Baseada em Cursor (Mais Confiável)
GET /api/users?cursor=eyJpZCI6MTIzfQ&limit=20
{
"data": [...],
"meta": {
"hasNext": true,
"nextCursor": "eyJpZCI6MTQzfQ"
}
}
Vantagem: Consistente com alterações concorrentes, performante em grandes conjuntos de dados. Desvantagem: Não permite pular para páginas arbitrárias.
Baseada em Keyset (Mais Performante)
GET /api/users?after_id=123&limit=20
Usa o ID do último item (ou outro campo ordenado) para buscar a próxima página. Similar à baseada em cursor, mas com parâmetros transparentes.
Recomendação: Use paginação baseada em cursor para feeds em tempo real e grandes conjuntos de dados. Use baseada em offset para painéis administrativos onde a navegação por páginas importa.
Filtragem e Ordenação
Filtragem
GET /api/users?status=active&role=admin
GET /api/users?created_after=2024-01-01
GET /api/users?search=alice
Para filtros complexos, considere um parâmetro de consulta dedicado:
GET /api/users?filter[status]=active&filter[role]=admin
Ordenação
GET /api/users?sort=name # Ascendente
GET /api/users?sort=-created_at # Descendente (prefixo com -)
GET /api/users?sort=-created_at,name # Múltiplos campos
Tratamento de Erros
Respostas de erro consistentes são essenciais para a usabilidade da API:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": [
{
"field": "email",
"message": "Must be a valid email address",
"value": "not-an-email"
},
{
"field": "age",
"message": "Must be between 0 and 150",
"value": -5
}
]
},
"meta": {
"requestId": "req_abc123"
}
}
Códigos de status HTTP a usar:
| Código | Significado | Quando |
|---|---|---|
| 200 | OK | GET, PUT bem-sucedido |
| 201 | Criado | POST bem-sucedido |
| 204 | Sem Conteúdo | DELETE bem-sucedido |
| 400 | Requisição Inválida | Erros de validação |
| 401 | Não Autorizado | Autenticação ausente/inválida |
| 403 | Proibido | Autenticação válida, permissões insuficientes |
| 404 | Não Encontrado | Recurso não existe |
| 409 | Conflito | Recurso duplicado, conflito de versão |
| 422 | Não Processável | Semanticamente inválido |
| 429 | Muitas Requisições | Limite de taxa excedido |
| 500 | Erro Interno | Erro inesperado do servidor |
Versionamento
Três abordagens:
Caminho da URL (Recomendado)
GET /api/v1/users
GET /api/v2/users
Vantagem: Explícito, fácil de rotear, fácil de testar.
Baseado em Header
GET /api/users
Accept: application/vnd.myapi.v2+json
Vantagem: URLs limpas. Desvantagem: Mais difícil de testar, menos descobrível.
Parâmetro de Consulta
GET /api/users?version=2
Vantagem: Fácil de testar. Desvantagem: Semântica de parâmetro opcional.
Recomendação: O versionamento por caminho da URL é a escolha mais prática. É explícito, cacheável e funciona com qualquer ferramenta HTTP.
Manipulação de Data e Hora
Sempre use ISO 8601 em UTC:
{
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-15T14:22:33Z",
"expiresAt": "2024-12-31T23:59:59Z"
}
Nunca use timestamps Unix nas respostas da API — eles são ambíguos (segundos vs milissegundos) e não são legíveis por humanos. Para mais informações sobre manipulação de timestamps, veja nosso guia de Unix Timestamps.
Limitação de Taxa
Comunique os limites de taxa nos headers de resposta:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 67
X-RateLimit-Reset: 1705312800
Retry-After: 30
Retorne 429 Too Many Requests quando o limite for excedido, com uma mensagem de erro clara e o header Retry-After.
HATEOAS (Opcional, Mas Poderoso)
Inclua links para recursos e ações relacionados:
{
"data": {
"id": "123",
"name": "Alice",
"links": {
"self": "/api/users/123",
"orders": "/api/users/123/orders",
"avatar": "/api/users/123/avatar"
}
}
}
Isso torna sua API autodocumentada e reduz a construção de URLs no lado do cliente.
FAQ
Devo usar a especificação JSON:API ou criar meu próprio formato?
A especificação JSON:API (jsonapi.org) fornece um padrão abrangente, mas pode ser verbosa para APIs simples. Para a maioria dos projetos, projetar um formato personalizado mais simples seguindo os padrões deste guia é mais prático. Use JSON:API se precisar de bibliotecas de cliente automáticas e de um padrão rigoroso.
Como devo lidar com atualizações parciais (PATCH vs PUT)?
Use PUT para substituição completa do recurso (o cliente envia todos os campos). Use PATCH para atualizações parciais (o cliente envia apenas os campos alterados). PATCH com JSON Merge Patch (RFC 7396) é a abordagem mais simples: envie um objeto JSON apenas com os campos a serem atualizados e null para remover um campo.
Recursos Relacionados
- Formatador JSON — Formate respostas de API para melhor legibilidade
- Guia de Validação com JSON Schema — Valide payloads de API com JSON Schema
- JWT Tokens Explicados — Proteja suas APIs com JSON Web Tokens