icardb
REST API design: métodos, status codes e padrões de URL
Voltar para artigosPROGRAMAÇÃO

REST API design: métodos, status codes e padrões de URL

Por Equipe Editorial Icardb 7 min de leitura

Conteúdo educativo. Sem patrocínio das ferramentas citadas. As recomendações refletem uso comum no mercado e não constituem indicação de compra ou contratação. Para decisões profissionais específicas, considere consultoria especializada.

REST não é um padrão rígido como SMTP ou HTTP/2. É um estilo arquitetural — um conjunto de restrições e convenções que, quando seguidas de forma consistente, produzem APIs previsíveis, cacheáveis e fáceis de manter. Este artigo cobre os métodos HTTP, tabela de status codes, design de URLs, versionamento, paginação e erros comuns observados em código de produção.

Restrições arquiteturais do REST

  • Cliente-servidor: separação de responsabilidades. O cliente conhece as URLs e os media types; o servidor conhece o armazenamento e a lógica de negócio.
  • Stateless: cada requisição contém toda a informação necessária. Não há sessão de servidor entre chamadas. Autenticação via token em cada request.
  • Cacheable: respostas devem indicar explicitamente se podem ser reutilizadas (headers Cache-Control, ETag, Last-Modified).
  • Interface uniforme: recursos identificados por URLs, representações manipuladas via representações (JSON, XML), mensagens autodescritivas.

Métodos HTTP e idempotência

Idempotência significa que fazer a mesma requisição múltiplas vezes produz o mesmo efeito no servidor que fazê-la uma vez. Isso é crítico para retentativas automáticas em caso de timeout de rede.

MétodoAçãoIdempotenteBody na requestUso típico
GETLerSimNão recomendadoBuscar recurso ou coleção
POSTCriarNãoSimCriar novo recurso; ações complexas
PUTAtualizar completoSimSimSubstituir recurso existente
PATCHAtualizar parcialSim*SimModificar campos específicos
DELETERemoverSimOpcionalExcluir recurso
HEADMetadadosSimNãoVerificar existência sem corpo
OPTIONSCapacidadesSimNãoCORS preflight

*PATCH teoricamente é idempotente, mas depende da implementação. Um PATCH que incrementa um contador não é idempotente; um PATCH que substitui campos específicos sim.

PUT vs PATCH na prática

http
PUT /produtos/123
Content-Type: application/json

{
  "id": 123,
  "nome": "Teclado Mecânico",
  "preco": 450.00,
  "estoque": 12
}

PATCH /produtos/123
Content-Type: application/json

{
  "preco": 399.00
}

PUT exige que o cliente envie a representação completa do recurso. Campos omitidos podem ser interpretados como nulos (depende da implementação). PATCH permite enviar apenas o delta, reduzindo payload e evitando race conditions em campos não modificados.

Status codes: os mais importantes

CódigoSignificadoQuando usar
200 OKSucesso genéricoGET, PUT, PATCH, DELETE bem-sucedidos
201 CreatedRecurso criadoPOST que cria um novo recurso
204 No ContentSucesso sem corpoDELETE bem-sucedido; PUT/PATCH quando não há nada a retornar
400 Bad RequestRequisição inválidaJSON malformado; campo obrigatório ausente; tipo errado
401 UnauthorizedNão autenticadoToken ausente, expirado ou inválido
403 ForbiddenSem permissãoUsuário autenticado, mas sem acesso ao recurso
404 Not FoundRecurso não existeURL ou ID inexistente
409 ConflictConflito de estadoE-mail duplicado; versão de recurso conflitante (optimistic locking)
422 UnprocessableValidação falhouSintaxe correta, mas semântica inválida (e.g., CPF inválido)
429 Too Many RequestsRate limitCliente excedeu limite de requisições
500 Internal ErrorErro no servidorExceção não tratada; use com moderação e log detalhado
502 Bad GatewayGateway inválidoServidor upstream respondeu com erro
503 Service UnavailableServiço indisponívelManutenção; sobrecarga; usar Retry-After

Regra de ouro: 401 é sobre quem você é (autenticação); 403 é sobre o que pode fazer (autorização). 400 vs 422: 400 quando o parser não consegue entender a requisição; 422 quando entende, mas os dados violam regras de negócio.

Design de URLs e recursos

  • Use substantivos no plural, não verbos: /pedidos em vez de /criarPedido ou /getPedidos.
  • Hierarquia representa relacionamento, não ação: /clientes/42/pedidos (pedidos do cliente 42).
  • Filtros, ordenação e paginação vão em query params: /produtos?categoria=teclados&ordenar=preco&direcao=asc.
  • Mantenha URLs estáveis. Se o recurso mudar de nome interno, o slug público deve continuar funcionando (redirect 301).
  • Não exponha IDs internos de banco (auto-incremento). Use UUIDs ou hashes publicamente previsíveis.

Exemplo: design de API de e-commerce

http
GET    /api/v1/produtos?categoria=monitores&page=2&limit=20
GET    /api/v1/produtos/550e8400-e29b-41d4-a716-446655440000
POST   /api/v1/pedidos
GET    /api/v1/pedidos/12345
PATCH  /api/v1/pedidos/12345/status
GET    /api/v1/clientes/42/pedidos

Versionamento de API

Há duas escolas principais: versionamento na URL (/api/v1/...) e versionamento via header (Accept: application/vnd.api+json;version=2). A URL é mais simples de testar, documentar e cachear. O header é mais 'puro' REST, mas dificulta debug e cache em proxies.

  • Para APIs públicas e equipes pequenas: versionamento na URL é recomendado por simplicidade.
  • Para APIs internas com gerador de SDK automático: versionamento por header pode ser viável.
  • Nunca quebre contratos dentro da mesma versão. Campos podem ser adicionados; nunca removidos ou alterados de tipo.
  • Mantenha versões antigas operacionais por pelo menos 6-12 meses com aviso de depreciação nos headers (Deprecation: true, Sunset: <data>).

Paginação: offset vs cursor

EstratégiaVantagemDesvantagemMelhor para
Offset + LimitSimples de implementar; permite pular para qualquer páginaLenta em offsets grandes (O(offset)); inconsistente se dados mudam durante navegaçãoAdministração; datasets pequenos (< 100k)
Cursor (keyset)Consistente sob mutação; escalávelNão permite pular para página arbitrária; requer índiceFeeds infinitos; grandes volumes; dados em tempo real

Cursor pagination usa o valor de um campo indexado (geralmente created_at + id) como ponto de partida: ?cursor=eyJpZCI6MTIzLCJjcmVhdGVkQXQiOiIyMDI2LTA0LTE1VDEwOjAwOjAwWiJ9. Isso evita o problema de registros 'pularem' quando novos itens são inseridos no topo.

Erros e formato de resposta

Respostas de erro devem ser consistentes. Um formato comum (inspirado em RFC 7807 Problem Details) inclui type, title, status, detail e instance.

json
{
  "type": "https://api.exemplo.com/errors/invalid-parameter",
  "title": "Parâmetro inválido",
  "status": 400,
  "detail": "O campo 'email' deve ser um endereço válido.",
  "instance": "/api/v1/clientes",
  "errors": [
    { "field": "email", "message": "formato inválido" }
  ]
}
  • Nunca exponha stack traces, SQL ou caminhos de arquivo internos em respostas de erro.
  • Inclua um correlation-id ou request-id em todo log e resposta para rastrear falhas em sistemas distribuídos.
  • Documente todos os códigos de erro possíveis para cada endpoint na documentação da API.

HATEOAS: links na resposta

HATEOAS (Hypermedia as the Engine of Application State) propõe que a resposta inclua links para ações possíveis. Na prática, poucas APIs públicas implementam HATEOAS completo porque aumenta payload e exige que o cliente interprete links dinamicamente. Uma abordagem pragmática é incluir links apenas para navegação (self, next, prev) e deixar ações explícitas na documentação.

json
{
  "data": [...],
  "links": {
    "self": "/api/v1/pedidos?page=2",
    "prev": "/api/v1/pedidos?page=1",
    "next": "/api/v1/pedidos?page=3"
  }
}

Perguntas frequentes

+Qual a diferença entre 401 e 403?

401 Unauthorized significa que a requisição não provou quem é o usuário (falta token, token inválido ou expirado). 403 Forbidden significa que o usuário está autenticado, mas não tem permissão para aquele recurso. Um visitante sem login recebe 401; um usuário comum tentando acessar painel de admin recebe 403.

+Devo usar PUT ou PATCH para atualizações?

Use PUT quando o cliente envia a representação completa do recurso e campos omitidos devem ser sobrescritos (ou nulificados). Use PATCH para atualizações parciais, enviando apenas os campos que mudaram. PATCH reduz payload e evita race conditions em campos não modificados.

+É errado retornar 200 para POST de criação?

Não é errado, mas 201 Created é mais preciso. 201 indica explicitamente que um novo recurso foi criado e permite que o cliente espere um header Location com a URL do recurso. 200 OK genérico funciona, mas perde semântica.

+Como versionar uma API que já está em produção?

Congele o comportamento da versão atual como v1. Implemente mudanças quebradoras em /v2/. Redirecione clientes com header Deprecation e Sunset. Nunca altere o comportamento de /v1/ após publicada — isso quebra contratos implícitos.

+Quando usar query params vs body na requisição?

Use query params para leitura (GET) — filtros, ordenação, paginação. Use body para escrita (POST, PUT, PATCH) — criação e atualização de recursos. Nunca envie dados sensíveis (senhas, tokens) em query params, pois ficam expostos em logs de servidor e histórico de navegador.

Fontes consultadas

Revisão editorial: publicado em . Última revisão em . Conteúdo educativo, sem patrocínio das ferramentas citadas.

Crédito da imagem: Ilustração editorial: Equipe Icardb

Leia também