API Paradigms
Compare API paradigms: REST, GraphQL, gRPC, and WebSocket for different use cases.
What are API paradigms?
An API paradigm is a set of conventions for expressing operations, data, interaction direction, state, errors, and evolution between software components. REST models HTTP resources, GraphQL exposes a typed query graph, gRPC defines typed remote procedures, webhooks push asynchronous notifications, and SSE or WebSocket maintain live streams.
The choice matters because it shapes client coupling, caching, tooling, query cost, connection state, delivery semantics, failure recovery, and compatibility. No paradigm is universally faster or simpler; the workload and operating contract decide whether its strengths are useful.
The invariant to remember
Every accepted interaction must have one defined meaning through success, timeout, retry, cancellation, version change, and recovery. Transport delivery is not the same as a durable business outcome.
Review communication patterns first if request-response, asynchronous messaging, and streaming are unfamiliar.
Choose an interaction shape before a protocol
One bounded result
Request-response
The client sends an operation and waits under a deadline for success or a typed failure. REST, GraphQL, SOAP, and unary gRPC commonly use this shape.
Provider pushes later
Asynchronous notification
The producer delivers an event after a durable change. Webhooks and message-backed APIs need retry, deduplication, ordering, and replay rules.
Many responses
Server stream
The server emits ordered updates to one request or subscription. SSE and gRPC server streams fit one-way live delivery with explicit cancellation and resume behavior.
Both sides send
Bidirectional session
A long-lived connection carries messages in both directions. WebSocket and bidirectional gRPC need connection identity, backpressure, heartbeats, drain, and recovery.
Choose the interaction contract before the protocol
Select a workload, compare paradigms, and change required capabilities. The goal is a defensible contract, not a universal protocol ranking.
Contract verdict
The paradigm matches the selected workload contract
Now specify identity, authorization, deadlines, errors, retries, versioning, observability, and overload behavior before choosing a framework.
Semantics
Define operation identity, ordering, consistency, idempotency, errors, cancellation, and completion before transport details.
Envelope
Bound payload, query cost, stream duration, fan-out, concurrency, retries, deadlines, and per-tenant resource use.
Evolution
Version schemas and behavior additively, test old clients, publish deprecation evidence, and keep rollback compatibility.
Use HTTP resource semantics deliberately with REST
REST-oriented APIs expose resources through stable identifiers and standard HTTP semantics. The useful design surface is larger than JSON over POST.
1 URI
Identify the resource
Use a stable resource identity that does not leak a temporary storage layout or implementation call stack.
2 Operation
Choose method semantics
Use safe and idempotent methods accurately; define conditional updates and repeated-write behavior for mutations.
3 Response
Return HTTP evidence
Use status, representation, validators, cache controls, pagination, rate-limit information, and typed error bodies consistently.
4 Infrastructure
Let intermediaries help
CDNs, browsers, proxies, gateways, and observability tools can use standard headers only when the API preserves their meaning.
Make cache identity complete
- Include authorization and representation dimensions that change the response.
- Use
Cache-Control, validators, andVaryconsistently across origin and intermediaries. - Do not cache personalized data under a shared key.
- Define invalidation and staleness in product terms, not only TTL duration.
Govern GraphQL as a query execution system
GraphQL lets clients select fields through a typed schema, which is valuable when clients need different connected views. That flexibility moves complexity into schema design, resolver execution, authorization, caching, and query governance.
depth
Traversal bound
Limit nested selection depth and recursive relationships before execution.
cost
Resource budget
Assign realistic cost to fields, cardinality, arguments, and fan-out rather than counting fields equally.
batch
Resolver access
Batch and cache per-request data loading to avoid N+1 calls and duplicate downstream work.
field
Authorization
Authorize object and field access using the caller, tenant, arguments, and parent context.
Persisted operations can reduce arbitrary query exposure and improve operational visibility. They do not remove the need to validate variables, authorize fields, and control resolver cost.
Use gRPC for typed service contracts, not magic performance
gRPC uses Protocol Buffers to define service methods and messages, then generates clients and servers. It supports unary, server-streaming, client-streaming, and bidirectional-streaming calls over HTTP/2.
One request produces one response. Propagate deadlines and cancellation, classify retryable status, and use operation IDs for state changes.
One request produces many messages. Bound stream lifetime, message rate, buffers, and cancellation; define replay or snapshot recovery.
Many client messages produce one response. Define partial acceptance, ordering, flow control, deduplication, and final commit semantics.
Both sides stream independently. Define message types, correlation, ordering, backpressure, half-close, authentication refresh, and reconnect behavior.
Generated types improve wire compatibility but do not define authorization, business validation, idempotency, or safe retries.
Push durable events through webhooks
A webhook provider calls a consumer-owned HTTP endpoint after an event. Providers should expect timeouts, retries, slow consumers, rotated credentials, and endpoints that disappear.
Webhook delivery is an asynchronous workflow
A `2xx` acknowledgement means the consumer accepted delivery under the documented contract; it does not automatically prove every downstream action completed.
Durable source
Event outbox
Records a stable event ID, type, resource version, occurrence time, and tenant after the business transaction commits.
Signed attempt
Delivery worker
Signs the exact payload and timestamp, applies endpoint policy, sends under a deadline, and records the attempt outcome.
Verify and deduplicate
Consumer inbox
Authenticates the signature, checks replay age, claims the event ID durably, validates schema, and acknowledges promptly.
Bounded recovery
Retry and quarantine
Retries transient failures with backoff and jitter, disables broken endpoints by policy, and exposes replay ownership.
Pick the simplest live transport that fits
Server to browser
SSE
Use when the browser only receives text events. Define event IDs, retention, retry hints, heartbeats, proxy timeouts, and snapshot recovery.
Full duplex
WebSocket
Use when both sides need low-latency messages. Define a versioned subprotocol, admission, per-client queues, backpressure, drain, and reconnect.
Typed services
gRPC stream
Use between compatible clients and services that benefit from generated streaming contracts and HTTP/2 flow control.
Bounded fallback
Polling
Use when update frequency is low or network infrastructure makes long-lived connections unreliable. Conditional requests can reduce unnecessary transfer.
Design the failure contract independently of transport
The same timeout can mean the server never received the request, rejected it, committed it but lost the response, or is still working. Retrying without stable operation identity can repeat a state change on REST, GraphQL, gRPC, SOAP, or a socket protocol alike.
Loading failure lab
Preparing API incidents...
Evolve schemas and behavior compatibly
Wire compatibility is necessary but not sufficient. A field can remain syntactically valid while its meaning, units, default, authorization, or lifecycle changes incompatibly.
1 Schema
Add, do not repurpose
Add optional fields or new enum handling. Never reuse an old field number, name, or value for a different meaning.
2 Migration
Read old and new
Deploy consumers that tolerate both shapes before producers emit the new form broadly.
3 Evidence
Measure active consumers
Track client and schema versions, unknown fields, deprecated operations, and compatibility failures by owner.
4 Lifecycle
Retire with a plan
Publish timelines, contact owners, provide migration tooling, test rollback, and remove old behavior only after evidence confirms safety.
Apply the shared production envelope
Every paradigm needs the same foundational controls, even when the framework names them differently.
Who may do what
Identity and policy
Authenticate clients and services, authorize every resource or field, bind tenant context, and prevent confused-deputy delegation.
Protect the service
Resource bounds
Limit payload, query complexity, stream duration, concurrency, fan-out, rate, retries, buffers, and downstream work per identity.
Make failure actionable
Typed outcomes
Distinguish validation, authorization, conflict, quota, dependency, timeout, cancellation, unknown outcome, and internal failure.
Operate
End-to-end evidence
Propagate trace and operation IDs; record route, schema, client, retries, queueing, upstream time, result, and business outcome.
Review the production checklist
Build these controls
- Define interaction direction, operation identity, ordering, completion, and failure semantics first.
- Use deadlines, cancellation, bounded retries, idempotency, backpressure, and admission consistently.
- Version wire shape and meaning additively; test old and new consumers during rollout.
- Authenticate and authorize at the resource, method, field, event, and connection boundaries that matter.
- Measure end-to-end latency, queueing, query cost, errors, retries, saturation, and business outcomes.
- Test lost responses, duplicate delivery, slow consumers, reconnect gaps, breaking producers, and rollback.
Avoid these traps
- Selecting a paradigm from invented universal latency or throughput rankings.
- Retrying state-changing operations because the transport call timed out.
- Treating generated types as business validation or authorization.
- Exposing unrestricted GraphQL queries, unbounded streams, or infinite webhook retries.
- Changing field meaning in place or retiring behavior without active-client evidence.