Teu progresso
0 / 83 módulos0%
Estágio 04 · 04-05
BloqueadoAPI 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.
Maioria dos APIs "REST" estão em Level 2. Level 3 raro mas robusto pra discovery.
Status codes (RFC 9110):
Cada um carrega significado. Não invente ou abuse 200 com error body.
/orders, não /order./orders/{id}/events./orders?status=pending&tenant=acme./orders/{id}/cancel (POST). Não force tudo em CRUD puro.Anti-padrões:
/getOrder./orders e /userList.?offset=20&limit=10. Simples mas problema em offsets grandes (DB skip lento) e instabilidade durante mudanças.?after=<cursor>&limit=10. Cursor é opaco (ID + timestamp encoded base64). Estável e fast.Sempre retorne metadata: { data: [...], nextCursor: "..." } ou via Link header (RFC 5988).
Em Logística com 100k pedidos por tenant, cursor é obrigatório.
?status=pending (igualdade).?createdAt[gte]=2026-01-01 (operadores).?sort=-createdAt,name (descendente, asc).?fields=id,name (sparse fieldsets).GraphQL elimina query string complexity. REST com convention forte pode chegar perto.
API que aceita header Idempotency-Key:
{key, response, ttl}.Estratégias:
/v1/orders, /v2/orders. Visível, simples.Accept: application/vnd.api.v2+json. Cleaner URLs, harder to discover.?version=2. Anti-pattern fraco.Em B2B / public APIs: versionado obrigatório.
Deprecation:
Deprecation: true + Sunset: <date> (RFC 8594).Que muda quebra cliente:
Não-breaking (geralmente):
Política: aditivo é safe; tudo mais é breaking.
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": [...]
}
Padrão emergente. Estável. Use.
Evite:
{success: false}.OpenAPI 3.1 (JSON Schema 2020-12 compatible) é o standard.
Workflows:
Code-first é mais ergonômico. Spec-first dá controle e disciplina mais forte.
Tools: Stoplight, Swagger UI, Redoc, Spectral (linter), Schemathesis (testing).
Trade-offs:
Quando vence:
Quando perde:
RPC sobre HTTP/2 + Protobuf:
Quando: backend ↔ backend perf-sensitive (microservices). Mobile pode usar.
Padrão moderno: gRPC interno + REST/GraphQL externo.
Duas alternativas modernas a REST/gRPC quando você controla cliente e servidor.
tRPC (TS-only)
Define procedures em TS, infere tipos no client por type-level magic. Sem code generation, sem schema separado.
// server
export const appRouter = router({
user: router({
byId: publicProcedure
.input(z.object({ id: z.string() }))
.query(({ input }) => db.user.findUnique({ where: { id: input.id } })),
update: protectedProcedure
.input(updateSchema)
.mutation(({ input, ctx }) => db.user.update({ ... })),
}),
});
export type AppRouter = typeof appRouter;
// client
const user = await trpc.user.byId.query({ id: 'abc' }); // tipado!
Trade-offs:
AppRouter type do server. Não funciona em deploys cross-repo sem ginástica.Quando vale tRPC:
Connect-RPC (Buf)
Sucessor moderno de gRPC-Web. Mantém Protocol Buffers como schema mas suporta JSON sobre HTTP/1.1 e gRPC sobre HTTP/2, no mesmo endpoint.
service UserService {
rpc GetUser(GetUserRequest) returns (User);
}
buf tooling (Buf Build): linting, breaking-change detection, schema registry, CI integration.fetch plain.// client TS gerado
const client = createPromiseClient(UserService, transport);
const user = await client.getUser({ id: 'abc' });
Trade-offs:
buf generate.Quando vale Connect-RPC:
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.
Especificação concreta pra REST com:
{type, id, attributes, relationships, links}.Pesado mas consistent. Algumas APIs públicos adotam.
Hypermedia: response inclui links pra próximas ações.
{
"id": "123", "status": "pending",
"links": {
"cancel": "/orders/123/cancel",
"self": "/orders/123"
}
}
Permite client descobrir transições válidas. Raramente adotado, mas elegante quando regras são complexas (workflow state machines).
Padrões:
acme.api.example.com.X-Tenant-Id./t/acme/orders.Decisão deve ser stable; mudar quebra all integrations.
API outbound (server → cliente). Protocolo:
Event-Id. Consumer deve dedupe via Redis SET com TTL (24h+) ou tabela webhook_processed(event_id PRIMARY KEY). Sem isso, retry duplica side effects (dobra cobrança, dobra notificação).OrderShipped chegando antes de OrderCreated.Cliente exposto a internet, recebendo. Documente:
Best practices: Stripe, GitHub, Shopify APIs são references — leia o doc deles em vez de inventar.
Boa API:
API pública sem isso = atrito alto.
Em organizações grandes:
Fragmentation problem: GraphQL monolithic vira gargalo. Federation resolve: schema unified composto de subgraphs, cada subgraph owned por team.
Apollo Federation v2:
@key(fields: "id")).@external, @requires, @provides, @override controlam ownership.# Orders subgraph
type Order @key(fields: "id") {
id: ID!
total: Float!
}
# Users subgraph
type User @key(fields: "id") {
id: ID!
orders: [Order] # resolved cross-subgraph
}
Schema stitching (deprecated em favor de Federation v2): juntava schemas via runtime delegation. Mais frágil; problemas de schema conflict.
Quando Federation faz sentido:
Quando Federation é overkill:
Alternativas: GraphQL Mesh, Hasura, mantém-monolith.
Apollo Router (Rust, 2022+) substituiu Apollo Gateway (Node) como o gateway de produção. Anatomia operacional:
Router pipeline em 1 query:
rover supergraph compose).Exemplo de query plan pra Logística:
query OrderWithCourier($id: ID!) {
order(id: $id) { # Orders subgraph
id
total
courier { # cross-subgraph
id
name # Couriers subgraph
rating
}
}
}
Plan gerado:
Fetch(Orders) {
query { order(id: $id) { id total courier { __typename id } } }
}
Flatten(path: "order.courier") {
Fetch(Couriers) {
query($representations: [_Any!]!) {
_entities(representations: $representations) {
... on Courier { name rating }
}
}
}
}
__typename + id (entity representation) é como subgraphs se referenciam — Couriers resolve Courier por _entities resolver baseado em @key(fields: "id").
Apollo Router em produção:
@defer em fragment lento), atualizações chegam via multipart HTTP.@cacheControl directives.# router.yaml
supergraph:
listen: 0.0.0.0:4000
introspection: false # production: false
traffic_shaping:
all:
timeout: 5s
subgraphs:
payment-service:
timeout: 10s # payment é mais lento
experimental_retry:
min_per_sec: 10
retry_percent: 0.2
telemetry:
exporters:
tracing:
otlp:
endpoint: http://collector:4317
Caveats reais 2026:
_entities batching (default) + DataLoader patterns dentro de cada subgraph.rover supergraph compose falha em conflitos (mesmo type definido em 2 subgraphs sem @shareable). Bloqueie merge.request.context.auth pra subgraphs. Subgraph pode fazer authz fina; mas duplicação de logic é risco.gRPC suporta 4 modos:
Use cases bidirectional:
service Chat {
rpc Connect(stream ClientMessage) returns (stream ServerMessage);
}
Client e server podem write/read independente. Multiplexed sobre HTTP/2.
Trade-offs vs WebSocket:
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.
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:
Costs:
Stripe usa internamente. Netflix popularizou.
API Gateway é layer entre clients e backend services:
Funções:
Implementations:
Patterns:
Anti-patterns:
Stripe não quebra API. Versioning:
2024-04-15 é versão. Stripe-Version header opt-in.Trade-off:
Lições:
Standard pra error response shape:
{
"type": "https://api.example.com/probs/insufficient-funds",
"title": "Insufficient Funds",
"status": 403,
"detail": "Your balance is R$ 12.34, but charge requires R$ 50.00.",
"instance": "/account/12345/charges/abc"
}
Campos:
type: URI identificando tipo do erro (semantic, ideal apontar pra docs).title: human-readable curto.status: HTTP status duplicado.detail: human-readable longo, this instance.instance: URI da ocorrência específica.+ extensões: campos custom.Content-Type: application/problem+json.
Vantagens:
type permite client lógica programática (não regex em string).Mesma operação "criar pedido" em 4 estilos:
REST (level 3):
POST /v1/orders
Content-Type: application/json
Idempotency-Key: abc-123
{ "items": [...], "address": {...} }
→ 201 Created
Location: /v1/orders/ord_xyz
{ "id": "ord_xyz", "_links": { "self": "...", "cancel": "..." } }
GraphQL:
mutation CreateOrder($input: CreateOrderInput!) {
createOrder(input: $input) {
order { id total status }
errors { field message }
}
}
gRPC:
service Orders {
rpc CreateOrder(CreateOrderRequest) returns (Order);
}
tRPC (TS-only):
trpc.orders.create.mutate({ items, address });
Trade-offs práticos:
Padrão maduro: gRPC entre serviços, REST público (com OpenAPI), GraphQL pra agregação BFF, tRPC em monorepo TS controlado.
HATEOAS = "Hypermedia As The Engine Of Application State". Roy Fielding (dissertation 2000) define como REST level 4 no Richardson Maturity Model. Ideia: client não hardcoda URLs; server retorna links relevantes por estado do recurso ("de placed, dá pra cancel ou assign_courier"). Realidade 2026: ~5% das public APIs implementam HATEOAS verdadeiro. Resto é REST-Lite (RPC sobre HTTP com JSON). Por que adoção mínima: client devs ignoram links e hardcodam mesmo assim; OpenAPI cobre discoverability suficiente.
Richardson Maturity Model:
POST /api)./orders, /orders/123).GET/POST/PUT/DELETE; 200/201/404).Maioria das APIs senta em Level 2; "REST-Lite" pragmático.
JSON:API spec 1.1 (jsonapi.org) — formato hypermedia padronizado pra JSON. Conventions: data envelope, included/relationships, links, meta, sparse fieldsets, sorting, pagination. Pros: consistência cross-team; tooling gerado (clients, OpenAPI). Cons: verbose; learning curve; ganho pequeno em times pequenos.
Exemplo response Logística order:
{
"data": {
"type": "orders",
"id": "ord-123",
"attributes": {
"status": "in_transit",
"createdAt": "2026-05-06T10:00:00Z",
"totalCents": 12500
},
"relationships": {
"courier": { "data": { "type": "couriers", "id": "cou-456" } },
"items": { "data": [{ "type": "items", "id": "i-1" }, { "type": "items", "id": "i-2" }] }
},
"links": {
"self": "/api/orders/ord-123",
"cancel": { "href": "/api/orders/ord-123/cancel", "method": "POST" }
}
},
"included": [
{ "type": "couriers", "id": "cou-456", "attributes": { "name": "João" } }
],
"links": { "self": "/api/orders/ord-123" }
}
Hypermedia prático em REST-Lite (Stripe-style) — não é JSON:API completo, mas expand extensivo pra relations:
GET /api/orders/ord-123?expand=courier,items.product
{
"id": "ord-123",
"status": "in_transit",
"courier": { "id": "cou-456", "name": "João" },
"items": [
{ "id": "i-1", "quantity": 2, "product": { "id": "prod-1", "name": "Box A" } }
]
}
Sem campo "links"; client conhece URLs via OpenAPI / docs.
HAL (Hypertext Application Language) — formato hypermedia leve; menos verbose que JSON:API. Spec: IETF draft; suporte amplo (Spring HATEOAS, AWS API Gateway opcional):
{
"id": "ord-123",
"status": "in_transit",
"_links": {
"self": { "href": "/orders/ord-123" },
"courier": { "href": "/couriers/cou-456" },
"cancel": { "href": "/orders/ord-123/cancel" }
},
"_embedded": {
"items": [{ "id": "i-1", "qty": 2 }]
}
}
HTMX 2.0+ renaissance — client hypermedia-driven; HTML response + atributos hx-* pra partial updates. Server retorna HTML fragments; sem mapeamento JSON-pra-DOM no client. Pattern Logística admin panel (Hono backend):
app.post('/orders/:id/cancel', async (c) => {
const id = c.req.param('id');
await db.update(orders).set({ status: 'cancelled' }).where(eq(orders.id, id));
// Retorna HTML fragment; HTMX faz swap no DOM
return c.html(`
<div id="order-${id}" class="order cancelled">
<span>${id} - cancelled</span>
<button hx-post="/orders/${id}/restore" hx-swap="outerHTML">Restore</button>
</div>
`);
});
Compelling pra: admin panels, CRUD-heavy apps, stacks simples. Trade-off: ruim pra UI altamente interativa (drag-drop, real-time collaborative).
Quando HATEOAS / hypermedia ganha:
Recomendação pragmática 2026:
expand.Logística applied stack:
expand pra relations; sem overhead HATEOAS.actions explícito por order (semi-HATEOAS pra state machine):
{ "id": "ord-123", "status": "assigned", "actions": ["pickup", "cancel"] }
Anti-patterns:
_embedded com 100+ items (response payload bloat; pagine).expand sem limit em depth (expand=a.b.c.d.e.f) — N+1 query trap./v1/orders) quando HATEOAS daria forward-compatibility.cancel em order já delivered) — confusão no client.Cruza com: §2.7 (REST levels); §2.20 (GraphQL Federation); §2.24 (versioning evolution); ../02-frontend/02-04-react-ecosystem.md (React, HTMX como alternativa); 04-08-services-monolith.md (BFF pattern complementary).
Spec-first venceu o ciclo 2024-2026. Times que tratam OpenAPI/Proto como fonte de verdade — não como documentation gerada depois do código — colhem SDKs typed, contract tests, mocks de dev, e linting CI quase de graça. Tooling ecosystem amadureceu: OpenAPI 3.2 alinhou com JSON Schema 2020-12, tRPC v11 estabilizou, Connect-RPC virou opção real pra browser + serviços, Pact 5 unificou em rust core, schemathesis 4 trouxe property-based fuzzing, MSW 2 padronizou mocking. Codegen pipelines (openapi-typescript 7 com oxc parser ~10x mais rápido que v6, Stainless e Fern gerando SDKs production-grade) são moat competitivo: provider que entrega SDK typed em 6 linguagens fecha venda; provider que entrega só Postman collection perde.
OpenAPI 3.2 (release Q3 2025) — adições principais: webhooks paths (callbacks formalizados como cidadãos primários, não anexo de operação), prefixItems pra tuples (JSON Schema 2020-12 alignment), reform de examples (uma forma única, deprecou example singular em vários contextos), parameter style improvements, e discriminator mais preciso pra oneOf/anyOf polimórfico. JSON Schema 2020-12 traz $dynamicRef, unevaluatedProperties, dependentSchemas — vocabulary explícito.
# openapi.yaml — OpenAPI 3.2 com webhooks, discriminator, prefixItems
openapi: 3.2.0
info:
title: Logistica Orders API
version: 2026-05-01
servers:
- url: https://api.logistica.dev/v2026-05-01
paths:
/orders:
post:
operationId: createOrder
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/CreateOrderRequest' }
responses:
'201':
description: Order criada
content:
application/json:
schema: { $ref: '#/components/schemas/Order' }
webhooks:
orderStatusChanged:
post:
operationId: orderStatusChanged
requestBody:
content:
application/json:
schema: { $ref: '#/components/schemas/OrderEvent' }
responses:
'2XX': { description: Webhook entregue }
components:
schemas:
OrderEvent:
oneOf:
- $ref: '#/components/schemas/OrderCreated'
- $ref: '#/components/schemas/OrderShipped'
- $ref: '#/components/schemas/OrderCancelled'
discriminator:
propertyName: type
mapping:
order.created: '#/components/schemas/OrderCreated'
order.shipped: '#/components/schemas/OrderShipped'
order.cancelled: '#/components/schemas/OrderCancelled'
Coordinates:
type: array
prefixItems:
- { type: number, description: longitude }
- { type: number, description: latitude }
minItems: 2
maxItems: 2
Spectral linting + breaking-change detection no CI:
# .spectral.yaml
extends: ['spectral:oas', 'spectral:asyncapi']
rules:
operation-operationId-unique: error
operation-success-response: error
no-$ref-siblings: error
contract-no-breaking-removal:
description: Endpoints removidos = breaking
given: $.paths
severity: error
then: { function: schema, functionOptions: { schema: { ... } } }
# CI: spectral lint openapi.yaml && oasdiff breaking prev.yaml openapi.yaml
tRPC v11 stable (Q4 2024, TypeScript v5+ obrigatório) — server-side caller, batching nativo via httpBatchLink, FormData/file uploads de primeira classe, inferRouterOutputs/inferRouterInputs cobrem todo router. Não cross-language — só monorepo TS.
// server/router.ts
import { initTRPC, TRPCError } from '@trpc/server';
import { z } from 'zod';
const t = initTRPC.context<Ctx>().create();
const authed = t.middleware(({ ctx, next }) => {
if (!ctx.user) throw new TRPCError({ code: 'UNAUTHORIZED' });
return next({ ctx: { ...ctx, user: ctx.user } });
});
export const orderRouter = t.router({
create: t.procedure
.use(authed)
.input(z.object({ items: z.array(z.string()).min(1), addressId: z.string().uuid() }))
.output(z.object({ id: z.string(), status: z.enum(['pending', 'paid']) }))
.mutation(async ({ input, ctx }) => orderService.create(ctx.user.id, input)),
byId: t.procedure
.input(z.string().uuid())
.query(({ input }) => orderService.findById(input)),
});
export type AppRouter = typeof orderRouter;
// client.ts — batching + types end-to-end
import { createTRPCClient, httpBatchLink } from '@trpc/client';
import type { AppRouter } from '../server/router';
export const trpc = createTRPCClient<AppRouter>({
links: [httpBatchLink({ url: '/trpc', maxURLLength: 2083 })],
});
const order = await trpc.create.mutate({ items: ['sku-1'], addressId: '...' });
// order.status: 'pending' | 'paid' — inferido, sem codegen
Connect-RPC 2026 (stable desde 2024) — protocolo gRPC-compatível que serializa Proto binário OU JSON sobre HTTP/1.1 e HTTP/2; browser-native via Connect-Web (sem proxy gRPC-Web), curl-friendly (JSON path). Server streaming OK; bidi limitado a HTTP/2. ConnectError com code enum compatível gRPC.
// orders.proto
syntax = "proto3";
package logistica.v1;
service OrderService {
rpc CreateOrder(CreateOrderRequest) returns (Order);
rpc StreamOrderEvents(StreamOrderEventsRequest) returns (stream OrderEvent);
}
// Connect handler (Node) + client (browser) — mesmo protocolo
import { ConnectRouter, ConnectError, Code } from '@connectrpc/connect';
import { OrderService } from './gen/orders_connect';
export default (router: ConnectRouter) => router.service(OrderService, {
async createOrder(req, ctx) {
if (!ctx.requestHeader.get('authorization'))
throw new ConnectError('missing auth', Code.Unauthenticated);
return await orderService.create(req);
},
async *streamOrderEvents(req) { for await (const ev of bus.subscribe(req.orderId)) yield ev; },
});
Contract testing stack:
can-i-deploy. Pact 5 (rust core unificado) consolidou JS/Java/Go/.NET/Python sobre mesmo motor. Use entre serviços que deploy independente; não use em monorepo onde tudo deploy junto (overhead sem benefício; TS types resolvem).// orders-consumer.pact.test.ts (Pact JS v13)
import { PactV4, MatchersV3 } from '@pact-foundation/pact';
const { like, regex, eachLike } = MatchersV3;
const provider = new PactV4({ consumer: 'web', provider: 'orders-svc' });
test('GET /orders/:id retorna order paid', async () => {
await provider
.addInteraction()
.given('order 42 exists com status paid')
.uponReceiving('get order 42')
.withRequest('GET', '/orders/42', b => b.headers({ Accept: 'application/json' }))
.willRespondWith(200, b => b.jsonBody({
id: regex(/^\d+$/, '42'),
status: regex(/^(pending|paid|shipped)$/, 'paid'),
items: eachLike({ sku: like('sku-1'), qty: like(1) }),
}))
.executeTest(async (mock) => {
const res = await fetch(`${mock.url}/orders/42`, { headers: { Accept: 'application/json' } });
expect(res.status).toBe(200);
});
});
# Provider verification + can-i-deploy gate
pact-provider-verifier --provider-base-url=http://staging.orders \
--pact-broker-url=$BROKER --provider=orders-svc --publish-verification-results
pact-broker can-i-deploy --pacticipant orders-svc --version $GIT_SHA --to-environment production
# schemathesis fuzz
schemathesis run https://staging.api.logistica.dev/openapi.json --checks all --hypothesis-max-examples 500
Mocking pipelines:
prism mock openapi.yaml sobe servidor HTTP que responde conforme schema + examples; usado em dev + tests E2E antes do backend existir.@mswjs/source ou openapi-msw. Manter handler manual quando spec existe = manutenção dupla.// mocks/handlers.ts — gerado de OpenAPI via openapi-msw
import { http, HttpResponse } from 'msw';
import { createOpenApiHttp } from 'openapi-msw';
import type { paths } from './gen/api';
const api = createOpenApiHttp<paths>({ baseUrl: 'https://api.logistica.dev/v2026-05-01' });
export const handlers = [
api.post('/orders', ({ response }) =>
response(201).json({ id: 'ord_mock', status: 'pending', items: [] })),
api.get('/orders/{id}', ({ params, response }) =>
response(200).json({ id: params.id, status: 'paid', items: [] })),
];
SDK generation — três tiers:
paths/components types; runtime fetch wrapper minúsculo (~2kb). Bom pra interno.@hey-api/openapi-ts) — fork comunitário do openapi-typescript-codegen, gera client + types + zod schemas; ativo, plugável.// openapi-typescript 7 + openapi-fetch
// $ npx openapi-typescript ./openapi.yaml -o ./src/gen/api.d.ts
import createClient from 'openapi-fetch';
import type { paths } from './gen/api';
const client = createClient<paths>({ baseUrl: 'https://api.logistica.dev/v2026-05-01' });
const { data, error } = await client.POST('/orders', {
body: { items: ['sku-1'], addressId: 'addr_42' },
});
// data: { id: string; status: 'pending' | 'paid' } | undefined — typed do OAS
Stack Logística aplicada: orders REST API mantida spec-first em openapi.yaml (OpenAPI 3.2 + JSON Schema 2020-12). CI roda spectral lint (style + breaking rules) e oasdiff breaking contra branch main — bloqueia PR com remoção de endpoint não-deprecated. Gera paths.d.ts via openapi-typescript 7 publicado como @logistica/orders-sdk (versionado por release date 2026-05-01). Fern gera SDKs públicos TS/Python/Go pra parceiros logísticos. Pact entre web → orders-svc e orders-svc → couriers-svc; broker gate can-i-deploy antes de prod. schemathesis fuzz contra staging em cron noturno. MSW handlers gerados do OAS pra Storybook + tests E2E — backend desenvolve em paralelo. Webhooks (order.created/shipped/cancelled) declarados em webhooks: paths, signature HMAC documentada em securitySchemes.
10 anti-patterns:
can-i-deploy é confiança falsa; provider quebra silencioso.--checks all — pula response_schema_conformance; encontra 5xx mas perde shape mismatches.webhooks: paths do OAS 3.2 — consumer não pode gerar SDK pra receber; perde validação de payload.Cruza com: §2.10 (OpenAPI intro foundation), §2.13 (tRPC/Connect intro), §2.7-§2.8 (versioning + deprecation), §2.18 (consumer experience + DX), §2.24 (Stripe versioning como referência de spec-first), 03-04 CI/CD (Spectral + oasdiff + can-i-deploy como gates), 03-01 testing (contract testing como categoria de teste entre unit e E2E), 04-08 §2.11 (service mesh consume contracts pra routing/policy).
Você precisa, sem consultar:
Redesign API pública do Logística v2 com profundidade.
Link header + body metadata.?status=&createdAt[gte]=&sort=-createdAt)./v1/... em todas rotas externas.Idempotency-Key em todos POST críticos. Redis-backed./v1/docs + Redoc.POST /v1/webhooks/{eventId}/replay (admin).webhook_deliveries com history e status.viewer retornando dashboard).@logistica/sdk.Threshold de Maestria
Acerte todas as 5 pra marcar o módulo como concluído. Sem pressa, sem timer. Tudo fica salvo no teu navegador.
Q1Por que cursor-based pagination supera offset/limit em tabelas grandes?
Q2Qual a justificativa principal para usar Idempotency-Key em endpoints POST?
Q3Quando GraphQL geralmente perde para REST?
Q4Qual é a vantagem do RFC 9457 (Problem Details) sobre erros JSON ad-hoc?
Q5Por que tRPC é considerado má escolha para API pública?
Destrava
04-05 é prereq dos seguintes módulos: