APIs & Communication Patterns
Why This Matters
Every system design involves defining how services talk to each other. Choosing the right communication pattern is one of the first trade-offs you'll make.
Synchronous Communication
REST (Representational State Transfer)
The default for most web APIs.
Key Principles:
- Stateless — each request contains all info needed
- Resource-oriented — URLs represent resources, not actions
- Standard HTTP methods — GET, POST, PUT, PATCH, DELETE
- Uniform interface — predictable, discoverable
Design Best Practices:
GET /api/v1/users → List users
GET /api/v1/users/{id} → Get user
POST /api/v1/users → Create user
PUT /api/v1/users/{id} → Replace user
PATCH /api/v1/users/{id} → Update user
DELETE /api/v1/users/{id} → Delete user
GET /api/v1/users/{id}/orders → User's orders (nested resource)
Pagination patterns:
- Offset-based:
?page=3&limit=20(simple, but skip is expensive) - Cursor-based:
?cursor=abc123&limit=20(efficient, used by FB/Twitter) - Keyset-based:
?after_id=12345&limit=20(good for sorted data)
Pros: Simple, well-understood, cacheable, great tooling Cons: Over-fetching/under-fetching, chatty for complex reads, no streaming
gRPC (Google Remote Procedure Call)
High-performance, strongly-typed service-to-service communication.
- Uses Protocol Buffers (protobuf) for serialization — binary, compact
- Built on HTTP/2 — multiplexing, streaming, header compression
- Code generation — client/server stubs generated from
.protofiles - 4 communication patterns:
- Unary (request-response)
- Server streaming (one request, stream of responses)
- Client streaming (stream of requests, one response)
- Bidirectional streaming
protobufservice UserService { rpc GetUser (GetUserRequest) returns (User); rpc ListUsers (ListUsersRequest) returns (stream User); } message User { string id = 1; string name = 2; string email = 3; }
When to use gRPC:
- Internal microservice communication (high throughput)
- Low-latency requirements
- Polyglot environments (auto-generated clients)
- Streaming data
When NOT to use:
- Browser clients (limited browser gRPC support, use gRPC-Web)
- Simple CRUD APIs (REST is simpler)
- When human readability matters (binary protocol)
GraphQL
Client-driven query language for APIs.
graphqlquery { user(id: "123") { name email orders(last: 5) { id total items { name price } } } }
Pros:
- No over-fetching or under-fetching — client asks for exactly what it needs
- Single endpoint — reduces number of round trips
- Strong type system with introspection
- Great for mobile apps (minimize data transfer)
Cons:
- Complex server implementation
- Caching is harder (no URL-based caching)
- N+1 query problem (use DataLoader)
- Rate limiting is harder (query complexity varies)
- Security: malicious deep queries (use query depth/complexity limits)
Used by: GitHub, Shopify, Facebook, Airbnb
Asynchronous Communication
Message Queues (Point-to-Point)
Producer → Queue → Consumer
- One message → one consumer (competing consumers pattern)
- Message removed from queue after processing
- Used for: task distribution, work queues, background jobs
- Examples: RabbitMQ, Amazon SQS, Redis lists
Pub/Sub (Publish-Subscribe)
Publisher → Topic → Subscriber A
→ Subscriber B
→ Subscriber C
- One message → multiple subscribers
- Subscribers get independent copies
- Used for: event broadcasting, notifications, fan-out
- Examples: Kafka, Google Pub/Sub, SNS, Redis pub/sub
Event Streaming
Producer → Event Log (append-only) → Consumer Group A
→ Consumer Group B
- Events are persisted in an ordered, immutable log
- Consumers can replay from any point
- Used for: event sourcing, data pipelines, analytics
- Examples: Apache Kafka, Amazon Kinesis, Pulsar
Real-Time Communication Patterns
Short Polling
Client: "Any new data?" → Server: "No"
(wait 5 seconds)
Client: "Any new data?" → Server: "No"
(wait 5 seconds)
Client: "Any new data?" → Server: "Yes, here!"
- Simple but wasteful — lots of empty responses
- Good for: simple dashboards with low update frequency
Long Polling
Client: "Any new data?" → Server: (holds connection open...)
(30 seconds later)
Server: "Yes, here's new data!"
Client: "Any new data?" → Server: (holds connection open...)
- More efficient than short polling
- Server holds request until data is available (or timeout)
- Good for: near real-time without WebSocket complexity
Server-Sent Events (SSE)
Client → Opens connection → Server pushes events continuously
- One-way — server to client only
- Built on HTTP — works through proxies, firewalls
- Auto-reconnection built into browser API
- Good for: live feeds, notifications, stock tickers
WebSockets
Client ↔ Server (full duplex, persistent)
- Bidirectional — both sides send anytime
- Requires connection management (heartbeats, reconnection)
- Good for: chat, gaming, collaborative editing
Choosing the Right Pattern
| Scenario | Pattern | Why |
|---|---|---|
| Public API | REST | Standard, cacheable, well-understood |
| Internal microservices | gRPC | Performance, type safety, streaming |
| Mobile BFF | GraphQL | Minimize data transfer, flexible queries |
| Background processing | Message Queue | Async, retry, backpressure |
| Event fan-out | Pub/Sub | Decouple producers from consumers |
| Real-time chat | WebSocket | Bidirectional, low latency |
| Live notifications | SSE or Long Polling | Server-to-client, simpler than WS |
| Data pipeline | Event Streaming | Replay, ordering, persistence |
API Design Principles for Interviews
Idempotency
- GET, PUT, DELETE — naturally idempotent
- POST — use idempotency keys for safe retries
POST /payments
Idempotency-Key: abc-123-def
Versioning
- URL:
/api/v1/users(most common, simple) - Header:
Accept: application/vnd.api.v1+json(cleaner, harder to use) - Query param:
/api/users?version=1(least common)
Rate Limiting Headers
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1625097600
Retry-After: 30
Error Response Format
json{ "error": { "code": "VALIDATION_ERROR", "message": "Email is required", "details": [ { "field": "email", "issue": "required" } ] } }
Resources
- 📖 DDIA Chapter 4: Encoding and Evolution
- 📖 "API Design Patterns" by JJ Geewax
- 🔗 gRPC official docs
- 🔗 GraphQL official docs
- 🎥 ByteByteGo — API Architectural Styles
- 🔗 Microsoft REST API Guidelines
Previous: 02 - Networking Essentials | Next: 04 - Data Storage Fundamentals