Domine a Arquitetura de API REST: Guia Completo para Desenvolver com Boas Práticas

Domine a Arquitetura de API REST: Guia Completo para Desenvolver com Boas Práticas

“`html





Dominando a Arquitetura de API REST


Arquitetura • REST • Contratos • Evolução

Dominando a Arquitetura de API REST

Quando REST é bem arquitetado, sua API fica previsível para o time e consistente para os consumidores:
recursos claros, contratos estáveis, mensagens úteis e evolução sem rupturas.

Contratos por recurso
Semântica HTTP
Erros acionáveis

1) Modele recursos antes de escrever endpoints

A primeira decisão não é “como vou chamar”, e sim o que a API representa.
Em REST, pense em recursos (coisas) e em relações entre eles.
Endpoint é consequência do seu modelo.

Nomes por domínio

Use substantivos com significado estável (ex.: orders, customers).

Sem “verbo” no caminho

Prefira /orders/{id} a /getOrder/{id}.

Relacione recursos

Use sub-recursos com critério (/customers/{id}/orders).

Checklist rápido:

  • Consigo explicar cada rota com “é uma coleção” ou “é um item”?
  • O nome do recurso muda conforme a implementação?
  • Existe consistência entre todos os módulos (ordens, pagamentos, catálogo)?

2) Use semântica HTTP para reduzir ambiguidades

Um contrato REST fica mais forte quando a semântica HTTP é respeitada. Isso simplifica
tratamento de erros, cache e comportamento do cliente.

  • GET: leitura sem efeitos colaterais. Para coleções, ofereça paginação e filtros consistentes.
  • POST: criação de recurso (ou ação que resulte em recurso novo). Retorne 201 quando criar.
  • PUT: substituição completa do recurso. Use quando fizer sentido exigir o estado inteiro.
  • PATCH: atualização parcial. Use quando o cliente precisa alterar campos específicos.
  • DELETE: remoção do recurso. Se for soft delete, expresse isso no contrato (ex.: deletedAt).

Status codes que realmente ajudam:

  • 200 OK: resposta de leitura ou atualização que não cria recurso novo.
  • 201 Created: criação bem-sucedida (inclua Location com a URL do recurso).
  • 204 No Content: deleção/atualização que não retorna corpo.
  • 400 Bad Request: validação falhou (mensagem explicando o problema).
  • 401 Unauthorized: sem autenticação válida.
  • 403 Forbidden: autenticado, mas sem permissão.
  • 404 Not Found: recurso inexistente (ou intencionalmente não visível).
  • 409 Conflict: conflito de estado (id duplicado, regra de negócio, concorrência).
  • 422 Unprocessable Entity: detalhe de validação de domínio (se você usar esse padrão).
  • 429 Too Many Requests: rate limit com retorno de tempo/limite.
  • 500/503: falha interna e indisponibilidade (com rastreabilidade).
Regra de ouro: se o cliente precisa tomar decisões, o HTTP deve entregar contexto suficiente.
Não “mascare” tudo como 400 ou 500.

3) Padronize payloads e contratos (JSON com disciplina)

Contratos previsíveis reduzem custo de integração. Eu gosto de definir um padrão de payload
que vale para toda a API: campos semânticos, consistência de tipos e resposta de erro com mesma forma.

Boas práticas de contrato:

  • Tipos estáveis: se um campo é string para IDs, continue string (evite alternar números e strings).
  • Não mude nomes sem versão: evolução via novos campos/recursos quando possível.
  • Campos obrigatórios bem definidos: documente o que é sempre retornado vs. condicional.
  • Datetime com timezone: use ISO-8601 (2026-04-28T13:45:00Z).
  • Paginação previsível: informe limit, offset (ou cursor) e metadados.
  • Validação consistente: erros de validação devem indicar exatamente o campo e a causa.

Formato de erro que eu recomendo: uma estrutura uniforme com código e detalhes por campo.

{
  "type": "https://yurideveloper.com.br/errors/validation",
  "title": "Validation failed",
  "status": 422,
  "traceId": "a9c2f1d3-6c44-4f1c-90b0-7c3d2c1aa11b",
  "errors": [
    {
      "field": "email",
      "code": "invalid_format",
      "message": "O e-mail informado não possui um formato válido."
    },
    {
      "field": "password",
      "code": "too_short",
      "message": "A senha deve ter pelo menos 8 caracteres."
    }
  ]
}

O que isso resolve: o consumidor sabe onde falhou (field), qual regra (code) e como reagir (message).

4) Evolua a API sem quebrar consumidores

A parte difícil não é lançar — é continuar evoluindo sem destruir integrações.
Eu foco em três frentes: compatibilidade, observabilidade e governança do contrato.

  • Evite rupturas por padrão:
    adicione campos novos; remova só quando houver migração definida.
  • Contratos de comportamento:
    documente idempotência, semântica de atualização e condições para status codes.
  • Idempotência quando fizer sentido:
    criação via POST pode ter retried requests; considere chaves idempotentes (ex.: Idempotency-Key).
  • Controle concorrência:
    use ETag e If-Match para prevenir “último write vence” indevido.
  • Deprecations claras:
    sinalize em resposta (headers) e em documentação; mantenha por um período.
  • Observabilidade:
    sempre retorne traceId (ou equivalente) para correlacionar logs e incidentes.

Exemplo: concorrência otimista com ETag

// Cliente
GET /orders/123
Accept: application/json
// Resposta
{
  "id":"123",
  "status":"paid",
  "updatedAt":"2026-04-28T10:11:12Z"
}
ETag: "orders-123@a1b2c3d4"

// Cliente tenta atualizar preservando estado
PATCH /orders/123
If-Match: "orders-123@a1b2c3d4"
Content-Type: application/json
{
  "status":"shipped"
}

// Se alguém alterou antes:
HTTP/1.1 409 Conflict
Content-Type: application/problem+json
Por que isso importa?
Porque contratos REST bons não só descrevem estrutura — eles descrevem regras de mudança e expectativas sob concorrência.



“`