Stateless HTTP-based β universal, cacheable. The default choice for public APIs
βΈ Anatomy of a REST URL
GET /api/v1/products/123 β Fetch (cacheable, idempotent)
POST /api/v1/orders β Create (use idempotency key to prevent dupes)
PUT /api/v1/orders/456 β Replace (idempotent)
DELETE /api/v1/orders/456 β Cancel (idempotent)
Pagination: ?cursor=abc123 (preferred) or ?page=2&limit=10
Caching: Cache-Control: max-age=3600 Β· ETag: "abc123"
Rate Limit: X-RateLimit-Limit: 1000 Β· X-RateLimit-Remaining: 847
Guarantees:Statelessness β no server-side session, any instance handles any request. Idempotency of GET/PUT/DELETE β safe to retry on failure. Cacheability β HTTP caching (CDN, browser) reduces load.
Real-world:Stripe API β gold standard (idempotency keys, versioning, pagination). GitHub API v3 β REST. Twilio β REST for SMS/voice.
Guarantees:Type safety β .proto schema + codegen catches incompatibility at compile time. Deadline propagation β timeout flows through entire call chain. Multiplexing β multiple concurrent calls on single HTTP/2 connection.
Real-world:Google internal comms. Netflix/Uber microservice-to-microservice. Best for: internal APIs, 10K+ RPS, bidirectional streaming. Not for browsers (use gRPC-Web proxy).
GraphQL
Client specifies exactly which fields β single endpoint, no versioning, strongly typed schema
Guarantees:No over-fetching β client gets only requested fields. Schema contract β server validates queries against schema before execution. Introspection β clients can discover available types/fields.
Risks:N+1 problem (fix with DataLoader batching). Deep query DoS (fix with depth limiting + cost analysis). Caching hard (each query unique). GitHub API v4, Shopify Storefront use GraphQL.
Async APIs
For long-running tasks β accept immediately, process in background, client polls for result
When to use: Image/video processing, report generation, ML inference, bulk imports β any operation that takes seconds to minutes. Don't make the client wait. Accept the request, queue the work, return a status URL.
βΈ Client calls GET /status periodically βΈ Simple β no infra needed βΈ Add Retry-After: 5 header βΈ Wasteful for long jobs Use: Short jobs, browser apps
Webhook
βΈ Server POSTs result to client URL βΈ No wasted requests β push βΈ HMAC signature for security βΈ Client needs public endpoint Use: Server-to-server, Stripe
WebSocket
βΈ Persistent conn, server pushes βΈ Instant notification β no delay βΈ Bidirectional (cancel jobs too) βΈ Connection management overhead Use: Real-time UIs, live progress
Real-world:Stripe β payment intents (202 β webhook on completion). AWS S3 β multipart upload (initiate β upload parts β complete). GitHub Actions β trigger workflow (202) β poll or webhook for result. Vercel β deploy (202) β poll build status.
Idempotent APIs
Same request N times = same effect as once. Critical for payments, orders, any operation that must not duplicate
βΈ Why Retries Are Dangerous
User transfers $100. Network glitch β client retries automatically. Without idempotency, the server deducts $100 twice. The user loses $200. This is the duplicate processing problem β and it happens in production.
β Safe β key marks partial, server resumes or rejects
Response lost in transit
β Unsafe
β Safe β key already processed, returns cached result
Implementation: Store keys in Redis with TTL (24h). Key = UUID, Value = {status, result}. On request: check key β if exists, return cached result β if not, process + store. Delete key after TTL. Stripe requires Idempotency-Key header on all POST endpoints.
SOAP
XML over HTTP β enterprise legacy contract style
Aspect
SOAP
Format
XML envelope (header + body)
Contract
WSDL β strict, machine-readable
Security
WS-Security (signed/encrypted parts)
Use today
Banking, telco, government, legacy ERP
SOAP
Protocol: Strict XML envelope (Header + Body) Contract: WSDL (machine-generated clients) Transport: HTTP, SMTP, JMS (transport-agnostic) Security: WS-Security (message-level encryption) State: Can be stateful (WS-ReliableMessaging) Verbose: 10-100Γ larger payloads than REST/JSON
When SOAP still wins:Banking/finance (WS-Security for signed transactions), Government (strict contracts, audit trails), Legacy integration (SAP, Oracle ERP). If you're building new: use REST or gRPC. If integrating with enterprise: expect SOAP.
Why heavier than REST: XML parsing, verbose envelope, mandatory schemas, stateful sessions.
CORS
Browser-enforced cross-origin policy
Header
Direction
Purpose
Example
Origin
Request β
Browser sends the requesting origin
Origin: https://app.com
Access-Control-Allow-Origin
β Response
Server declares which origins are allowed
* or https://app.com
Access-Control-Allow-Methods
β Response
Allowed HTTP methods
GET, POST, PUT, DELETE
Access-Control-Allow-Headers
β Response
Allowed custom headers
Authorization, Content-Type
Access-Control-Max-Age
β Response
Cache preflight result (seconds)
86400 (24 hours)
Access-Control-Allow-Credentials
β Response
Allow cookies/auth headers
true (cannot use with * origin)
Simple vs Preflight:Simple requests (GET/POST with standard headers) go directly β browser adds Origin, checks response. Preflight (PUT/DELETE, custom headers, non-standard Content-Type) triggers an OPTIONS request first. Server must respond with allowed methods/headers before browser sends the real request.
Common CORS mistakes:Using * with credentials (browsers reject this). Forgetting OPTIONS handler (preflight fails β request blocked). Not caching preflight (Max-Age=0 β OPTIONS on every request = 2Γ latency). Reflecting Origin without validation (security vulnerability β allows any site).
Persistent bidirectional β server pushes instantly. ~100K conn/server.
SSE
Server β Client
~ms
AI token streaming (ChatGPT, GitHub Copilot), live news tickers, CI/CD build logs
Auto-reconnect built into browser. Event ID for resuming. Text-only.
WebRTC
Peer-to-peer
Ultra-low
Video/audio calls (Zoom, Google Meet), screen sharing, Discord voice
Direct P2P β no server bandwidth for media. Browser-enforced encryption.
Webhook
Server β Your Server
~sec
Payment events (Stripe), CI/CD triggers (GitHub Actions), order updates (Shopify)
Event-driven HTTP POST β fire-and-forget. Retries on failure. No persistent connection.
Real-world:Slack β WebSocket for messaging. Figma β WebSocket for collab editing. Zoom β WebRTC for video. Robinhood β WebSocket for live stock prices. Socket.IO β auto-fallback to polling. ChatGPT β SSE for token streaming. Stripe β Webhook for payment events.
WebSocket
Persistent, bidirectional communication. Perfect for real-time apps that need instant two-way data flow.
βΈ Quick Summary
β Strengths
β Challenges
β Best Practices
Bidirectional instant (~ms) 100K+ connections/server Binary + text Persistent connection
Stateful (track clients) Needs reconnect logic Load balance complexity No auto-replay on disconnect
Use wss:// (TLS) Exponential backoff Redis Pub/Sub for scaling Validate messages server-side
βΈ Scaling with Redis Pub/Sub
Problem: Multiple servers β events isolated per server β clients on Server B miss updates from Server A. Solution: All servers subscribe to Redis channels β event fans out to ALL servers β all clients see updates instantly.
Load Balancing:Sticky sessions (pin client to server) vs connection migration (client reconnects) vs shared Redis store (state survives server change).
One-way server-to-client push over HTTP. Built-in auto-reconnect, event IDs for replay, automatic browser handling.
Middle ground: Polling is wasteful (99% empty requests) β WebSockets overkill if unidirectional β SSE perfect for server-only push with auto-reconnect built-in.
βΈ SSE Event Stream Format
βΈ Auto-Reconnection with Event Replay
βΈ Quick Summary
β Strengths
β Limitations
β Common Fixes
Auto-reconnect built-in Event replay (Last-Event-ID) Standard HTTP 10K+ connections/server
Server-only (unidirectional) Text-only (no binary) 6 conn/domain (HTTP/1.1) Proxy buffering issues No IE support
Proxy buffering: proxy_buffering off Connection limits: Use HTTP/2 Idle timeout: Heartbeat every 30s Storms: retry: 5000ms
βΈ Use Cases
Use
Example
Live prices / market data
Robinhood, Finnhub, Binance
AI token streaming
ChatGPT, GitHub Copilot
Build logs, CI/CD output
GitHub Actions, Jenkins, CircleCI
Live notifications
Gmail, Slack, email
Performance:10Kβ100K connections/server, 2β5KB memory/connection, ~10 bytes overhead vs 400+ for HTTP.
WebSocket vs SSE β Design Choices
When to pick each technology based on application requirements
βΈ Feature Comparison Matrix
Feature
WebSocket
SSE
Long Polling
Communication
Bidirectional β
Server only
Client asks repeatedly
Protocol Overhead
2-14 bytes/msg
10-50 bytes/msg
400+ bytes/msg
Browser Support
All modern (IE10+)
All modern (no IE)
Universal
Binary Support
β Yes
β Text only
β Yes
Auto-Reconnect
Manual required
Built-in browser
Built-in (polling loop)
Message Replay
Manual required
Built-in (Last-Event-ID)
No standard
HTTP/2 Multiplexing
No (separate connection)
β Yes (single connection)
β Yes (http requests)
Stateful
Very (per-client state)
Mostly (stream state)
Stateless
Proxy Friendly
Sometimes blocked
β Standard HTTP
β Standard HTTP
Connections/Server
100Kβ500K
10Kβ100K
1Kβ10K
Latency
~1-50ms
~100-200ms
~0.5-5s
Memory/Connection
5-20KB
2-5KB
Minimal
βΈ Decision Matrix: Which to Use?
β Use WebSocket When:
βΈ Client β Server messaging needed βΈ High frequency updates (100s/sec) βΈ Low latency critical (<10ms) βΈ Binary data needed βΈ Multiplayer games, trading apps βΈ Real-time collaboration (Figma) βΈ Chat apps (Slack, Discord) βΈ Live stock/crypto prices
β Use SSE When:
βΈ Server β Client only (no client send) βΈ Auto-reconnect needed (free feature) βΈ Event replay on disconnect βΈ Simple browser API (EventSource) βΈ AI token streaming (ChatGPT) βΈ Build logs (GitHub Actions) βΈ Live notifications / dashboards βΈ Text/JSON data only
β Use Long Polling When:
βΈ Serverless environment (timeouts) βΈ IE support required βΈ WebSocket blocked by proxy/firewall βΈ Simple infrequent updates OK βΈ Existing polling infrastructure βΈ Cost sensitive (minimal server state) βΈ Doesn't need real-time urgency βΈ Stateless is a hard requirement
βΈ Hybrid Approaches
SSE + HTTP POST: Use SSE for server-to-client push, regular POST for client commands (e.g., Twitch chat, YouTube comments) WebSocket + REST fallback: Try WS first, fallback to long polling if blocked (Socket.IO does this) WebSocket + Redis: For scale β WS per client, Redis Pub/Sub for multi-server broadcast (Slack, Figma pattern) WebSocket + Kafka: For event sourcing β all events stored in Kafka, clients subscribe via WS (high-scale trading systems)
βΈ Common Failure Scenarios
Scenario
WebSocket Impact
SSE Impact
Network disconnection
Connection drops, client must reconnect + resync state
Browser auto-reconnects, replays events via Last-Event-ID β
Server restart
All clients lose connection, must reconnect
Clients reconnect, get missed events if stored β
Proxy timeouts (>60s idle)
Connection dies, must detect + reconnect
Heartbeat prevents timeout β
High load spike
100K+ connections: high memory, CPU consumed
Fewer connections, easier to scale with multi-server β
Message ordering
Not guaranteed across reconnects
Event IDs allow ordering verification β
Browser refresh
Connection lost, full state resync needed
Can optionally restore via session storage + server replay β
Key Insight: SSE excels at resilience (auto-reconnect, event replay), WebSocket excels at latency & bidirectionality. Most real-time apps benefit from a hybrid approach: SSE for notifications, WebSocket for interactive features.