API design: contracts that survive retries
Choose an API shape, a live-update transport and a pagination contract; evolve clients safely and make retries safe.
20 min7 steps
By the end you can
- Choose REST, gRPC or GraphQL for a concrete client contract
- Choose WebSockets, SSE or long polling for live updates
- Explain offset drift and cursor pagination requirements
- Preserve compatibility while evolving an API
- Scope and persist idempotency keys to make retries safe
Choose the contract shape
REST
- Resource identifiers
- HTTP method semantics
gRPC
- Typed service methods
- Generated clients; streaming
GraphQL
- Typed graph schema
- Client selects response fields
REST is an architectural style, not just JSON over HTTP. gRPC exposes typed procedures. GraphQL lets clients select graph fields; the server still owns authorization, query cost and data access.
Keep flexible queries bounded
REST resources
- GET product
- GET reviews
GraphQL operation
- Select product and reviews
- Resolvers may still fan out
A single GraphQL request can trigger many backend reads. Field selection can reduce overfetching, but query depth, resolver batching, authorization and cost limits remain server responsibilities.
Pick a live-update transport
WebSocket
- Two-way messages
- Long-lived connection
SSE
- Server to browser events
- HTTP event stream; reconnect
Long polling
- Hold response until update
- Client issues next request
WebSockets suit two-way interaction. SSE suits server-driven feeds; browser EventSource can reconnect and carry event IDs. Long polling holds an ordinary HTTP request until an event or timeout, then repeats. Every option needs reconnect and overload behavior.
Notice offset drift
Page 1
Read A, B at offset 0
Insert
X arrives before A
Page 2
Offset 2 returns B, C: B repeats
Offset pagination names a position that can move. A cursor names a boundary in a stable total order, such as (created_at, id). Fetch after that boundary using the same filters and ordering. Cursors avoid this insertion shift, but do not create a snapshot.
Evolve without stranding clients
Usually additive
- New optional field
- Old clients tolerate unknown fields
Breaking
- Rename required field
- Change meaning or type
Compatibility includes behavior, not just schemas. Additive fields are safe only when readers tolerate them. New enum values can break exhaustive clients. Use a migration window and explicit version boundary for incompatible changes; retire old versions only after client migration.
Retry one logical operation
An idempotency key identifies one logical operation. Scope it to the caller and operation, bind it to the request payload, and retain its result. Concurrent requests require atomic deduplication with the effect. Reusing a key with a different payload must be rejected.
Expose the retry boundary
t0
K creates one order
t0 + 1 hour
Retry K replays result
t0 + 25 hours
24-hour retention has expired
Deduplication is bounded by retention. After expiry, the same key may be treated as new. Persist a business identifier when duplicate effects must remain impossible beyond that window. A transport retry policy cannot supply permanent exactly-once business behavior.
Loading question…
Mastery check
5 questions on fresh variants. One attempt each, no time limit. Score 80% or more to master the lesson.
Answer this step to continue.