API mal desenhada vira dívida que não some. Endpoints inconsistentes, status codes inventados, paginação por offset em tabelas grandes, filtros com query strings sem schema, versioning ausente, breaking changes sem deprecation, GraphQL adotado pra projeto que não precisa, gRPC obrigando time inteiro a virar polyglot. Cada decisão tem custo de longo prazo.
Este módulo é design API com profundidade: REST com maturity, paginação, filtragem, idempotência, versioning, documentation, evolução, GraphQL trade-offs, gRPC casos de uso, JSON:API spec, OpenAPI, REST hypermedia, Problem Details. Você sai sabendo desenhar API que sobrevive 3+ anos sem rewrite.
2. Teoria Hard
2.1 REST maturity (Richardson)
Level 0: HTTP como transporte (SOAP-style RPC over HTTP).
Level 1: resources (URLs separadas por entidade).
Level 2: HTTP verbs (GET/POST/PUT/DELETE com semantics) e status codes (200/201/204/4xx/5xx).
Level 3: HATEOAS, responses com links pra ações próximas.
Maioria dos APIs "REST" estão em Level 2. Level 3 raro mas robusto pra discovery.
2.2 Verb semantics e idempotência
GET: safe (sem efeito) e idempotent.
HEAD: idem GET sem body.
OPTIONS: descrever recurso (CORS preflight).
POST: cria. Não idempotent sem idempotency key.
PUT: substitui. Idempotent.
PATCH: atualiza parcial. Não necessariamente idempotent.
DELETE: remove. Idempotent.
Status codes (RFC 9110):
200 OK, 201 Created, 202 Accepted, 204 No Content.
301 Moved Permanently, 302 Found, 304 Not Modified.
400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 409 Conflict, 412 Precondition Failed, 422 Unprocessable Entity, 429 Too Many Requests.
500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable, 504 Gateway Timeout.
Cada um carrega significado. Não invente ou abuse 200 com error body.
2.3 Resource design
Plural consistente: /orders, não /order.
Hierarchy: /orders/{id}/events.
Filtering via query: /orders?status=pending&tenant=acme.
Action endpoints quando recurso não modela: /orders/{id}/cancel (POST). Não force tudo em CRUD puro.
Anti-padrões:
Verb em path: /getOrder.
Inconsistência: /orders e /userList.
POST pra leitura por preferência (CSRF excuse, use cookie SameSite ou JWT).
2.4 Pagination
Offset/limit: ?offset=20&limit=10. Simples mas problema em offsets grandes (DB skip lento) e instabilidade durante mudanças.
Cursor-based: ?after=<cursor>&limit=10. Cursor é opaco (ID + timestamp encoded base64). Estável e fast.
Keyset pagination: cursor é PK + sort key. Mesma idéia.
Sempre retorne metadata: { data: [...], nextCursor: "..." } ou via Link header (RFC 5988).
Em Logística com 100k pedidos por tenant, cursor é obrigatório.
2.5 Filtering, sorting
Conventions:
?status=pending (igualdade).
?createdAt[gte]=2026-01-01 (operadores).
?sort=-createdAt,name (descendente, asc).
?fields=id,name (sparse fieldsets).
JSON:API spec normalizes isso.
GraphQL elimina query string complexity. REST com convention forte pode chegar perto.
2.6 Idempotency keys
API que aceita header Idempotency-Key:
Server stores {key, response, ttl}.
Mesma key → retorna response anterior.
Stripe popularizou pattern.
2.7 Versioning
Estratégias:
URL: /v1/orders, /v2/orders. Visível, simples.
Header: Accept: application/vnd.api.v2+json. Cleaner URLs, harder to discover.
Query param: ?version=2. Anti-pattern fraco.
Não versionar: APIs que evoluem aditivamente sem breaking. Stripe famously avoids URL versioning, lets clients pin via dated header.
Adicionar status code aceito (cliente bem-feito ignora desconhecidos).
Política: aditivo é safe; tudo mais é breaking.
2.9 Error responses
Problem Details (RFC 9457):
{
"type": "https://example.com/probs/order-locked",
"title": "Order is currently locked",
"status": 409,
"detail": "Order #123 is being processed by another courier",
"instance": "/orders/123",
"errors": [...]
}
json
Padrão emergente. Estável. Use.
Evite:
200 OK com {success: false}.
Strings de erro inconsistentes.
Stack traces em prod.
2.10 OpenAPI / spec-first
OpenAPI 3.1 (JSON Schema 2020-12 compatible) é o standard.
Workflows:
Code-first: gerar OpenAPI a partir do código (Fastify schemas, NestJS, FastAPI).
Spec-first: escrever OpenAPI YAML primeiro, gerar code stubs ou validate em runtime.
Code-first é mais ergonômico. Spec-first dá controle e disciplina mais forte.
Schema separado (.proto). Refactor menos fluido que tRPC.
Code gen step. CI precisa rodar buf generate.
Curva de aprendizado de protobuf, vale o investimento se você vai consumir cross-language.
Quando vale Connect-RPC:
Multi-language clients (mobile native + web + serviços internos em Go/Java).
Você já tem cultura de schema-first.
Quer migrar de gRPC tradicional sem perder schema mas ganhar HTTP/JSON debugability.
Public API que vai ter vendor SDKs em várias linguagens.
Comparação rápida:
Aspecto
tRPC
Connect-RPC
gRPC clássico
REST + OpenAPI
Schema
Type-level TS
.proto
.proto
OpenAPI YAML
Cross-language
Não
Sim
Sim
Sim
Code gen
Não
Sim
Sim
Opcional
HTTP/JSON debug
Sim
Sim
Não (binary)
Sim
Setup overhead
Mínimo
Médio
Alto
Médio
Refactor velocity
Excelente
Bom
OK
Manual
Public API
Ruim
OK
Ruim (binary)
Excelente
Browser support
Nativo
Nativo (Connect-Web)
gRPC-Web (envoy proxy)
Nativo
Em monorepo TS médio, tRPC é game-changer. Em sistema multi-language ou API pública, Connect-RPC é o moderno; gRPC clássico ainda vence em latency-extreme.
Em web app, gRPC-Web limita streams (only server stream); use WS pra bidirectional. Em backend-to-backend ou mobile-to-backend, gRPC bidirectional é elegante.
2.22 BFF pattern (Backend for Frontend)
BFF = layer de API dedicada per-frontend (web, mobile, partner). Reduz over/under-fetch.
Mobile App → Mobile BFF → Order Service
→ User Service
→ Recommendation Service
Web App → Web BFF → mesmas downstream services
BFF agrega, formata, filtra pra cliente específico. Diferente de API Gateway (gateway é generic; BFF é client-specific).
Benefits:
Mobile vê resposta otimizada (campos limitados, payload pequeno).
Web vê resposta rica (mais fields, joined).
Partners vêm versão estável separada de web.
Auth + rate limit per-client.
Costs:
N BFFs = N codebases.
Frontend team frequentemente own BFF (Conway).
Duplicação se não cuidado.
Stripe usa internamente. Netflix popularizou.
2.23 API gateway placement e função
API Gateway é layer entre clients e backend services:
Funções:
Auth + token validation.
Rate limiting + quota.
Request routing.
Protocol translation (REST ↔ gRPC).
Response transformation.
Request/response logging.
Caching (CDN-ish).
Implementations:
AWS API Gateway, Cloud API Gateway (managed).
Kong, Tyk (self-hosted).
Envoy (mesh-native).
Nginx + Lua.
Patterns:
Edge gateway: terminação TLS, WAF, geo routing.
Service mesh sidecar: Istio, Linkerd (proxy per pod).
API gateway clássico: layer único, reverse proxy.
Anti-patterns:
Gateway com lógica de negócio (vira monolítico hidden).
Gateway que conhece schema interno detalhado (acoplamento).
2.24 Versioning evolution real, Stripe blog case
Stripe não quebra API. Versioning:
Date-based: 2024-04-15 é versão. Stripe-Version header opt-in.
Forever-stable: nunca remove campo, só adiciona ou marca deprecated em new version.
Internal middleware translates entre versões: client pede 2020-08-27 → middleware traduz response do current code pra format 2020-08-27.
Customer pinned em versão original; upgrade voluntário.
Trade-off:
Massive complexity em código (every change considera impact em N versões).