Skip to main content

Command Palette

Search for a command to run...

APIs & Communication: How Services Talk to Each Other

Updated
16 min readView as Markdown
APIs & Communication: How Services Talk to Each Other

Systems Design

# Post What it covers
00 APIs & Communication: How Services Talk to Each Other ← you are here How services talk to each other shapes everything about a system. Nine concepts covering REST, WebSockets, async patterns, and API gateways. (146 chars)
01 API Design: Building Contracts That Last A great API is a contract that outlasts your code. Here are the principles that make APIs intuitive to consume, safe to evolve, and cheap to maintain. (154 chars)
02 REST APIs: Constraints That Create Benefits REST isn't just HTTP with JSON. It's an architectural style with specific constraints — and understanding them explains why REST APIs are designed the way they are. (166 chars)
03 Authentication vs Authorisation: Two Questions, Two Checks Authentication is who you are. Authorisation is what you're allowed to do. Confusing them is one of the most common security mistakes in system design. (153 chars)
04 Session vs Token Authentication: Stateful vs Stateless Identity Session auth stores identity on the server. Token auth encodes it in the token. Here's how each works, where each breaks, and how to choose. (144 chars)
05 OAuth 2.0 & OpenID Connect: Delegated Access and Federated Identity OAuth 2.0 lets users grant apps access without sharing passwords. OpenID Connect adds identity on top. Here's how both actually work. (137 chars)
06 JWT: What's Actually Inside the Token JWTs are everywhere in modern auth — and frequently misused. Here's exactly what a JWT contains, how the signature works, and what it doesn't protect. (153 chars)
07 WebSockets: Real-Time Bidirectional Communication HTTP is request-response. WebSockets are a persistent two-way channel. Here's how they work, when to use them, and what to watch out for at scale. (151 chars)
08 Long Polling, SSE & Webhooks: The Server-Push Spectrum Three patterns for server-push communication — long polling, server-sent events, and webhooks. Here's how each works and when to reach for each. (150 chars)
09 Sync vs Async Communication: The Architectural Fork Synchronous services couple tightly. Asynchronous services decouple — but add complexity. Here's how to reason about which your system needs. (147 chars)
10 API Gateways: One Entry Point, Every Cross-Cutting Concern An API gateway centralises auth, rate limiting, routing, and observability for all your services. Here's what it does, how it works, and when you need one. (158 chars)
11 APIs & Communication: Wrap-Up A complete recap of all ten API and communication concepts — REST, auth, JWT, WebSockets, webhooks, async patterns, and API gateways — and how they connect. (161 chars)

APIs & Communication: How Services Talk to Each Other

The scenario

Your URL shortener is growing into a platform. It now needs a public API for third-party developers, a mobile app talking to the same backend as the web interface, an analytics service that processes redirect events, a partner integration that gets notified when new short links are created, and an admin dashboard that needs real-time redirect counts.

Five different communication needs. Five different answers. The public API needs consistent, predictable design. The mobile and web clients need authentication that's secure and usable. The analytics service needs to consume events without slowing down the redirect path. The partner integration needs to be notified without polling. The dashboard needs live data without refreshing.

None of these are solved by the same mechanism. Each represents a distinct communication pattern with its own tradeoffs — and picking the wrong one for a use case creates either a brittle system, a slow one, a hard-to-maintain one, or all three.

This pillar is about making those choices deliberately. The Networking pillar covered the medium data travels through. This pillar covers the contracts services make with each other about what they send, how they send it, and who is allowed to do what.

TL;DR: API design principles determine whether your API is a pleasure or a nightmare to build against. REST defines the most widely used architectural style for HTTP APIs. Authentication and authorisation are distinct concerns that must be solved separately. Session and token auth make different tradeoffs for different deployment models. OAuth and OpenID Connect are the industry standards for delegated access and federated identity. JWT is the token format most of the above relies on. WebSockets enable bidirectional real-time communication. Long polling, SSE, and webhooks are the spectrum of server-push patterns. Sync vs async communication is the architectural choice that affects everything downstream. API gateways are the traffic management layer that ties it all together.


What this pillar covers

API Design — the contract that outlasts your code

An API is a contract. Once external consumers depend on it, breaking it costs trust, engineering time, and sometimes revenue. Good API design isn't about aesthetics — it's about building contracts that are intuitive to consume, safe to evolve, and honest about what they do.

The principles that make an API good — consistent naming, sensible defaults, clear error messages, predictable versioning — are the same principles that make it cheap to maintain. An API that requires reading documentation for every call is an API your consumers will abandon for a competitor's.

Best mental model: a well-designed API is like a well-designed power socket — the interface is standardised, the behaviour is predictable, and you can plug in any compatible device without reading a manual. A poorly designed API is like a power socket that sometimes delivers 240V, sometimes 110V, and documents this as "voltage is context-dependent."


REST APIs — the dominant architectural style

REST (Representational State Transfer) is an architectural style — a set of constraints — for building distributed hypermedia systems. In practice, it's the dominant pattern for HTTP APIs: resources identified by URLs, manipulated through HTTP methods, with stateless requests and standard status codes.

REST is so widely adopted that "API" and "REST API" are used interchangeably in many contexts. Understanding REST's constraints — and understanding which of them most "REST APIs" actually violate — is the foundation for reasoning about API design, HTTP caching, and service interoperability.

Best mental model: REST treats your backend as a collection of resources — a URL shortener has links, users, and analytics. REST says: use standard HTTP verbs to manipulate those resources, use standard status codes to communicate outcomes, and keep each request self-contained. Follow those constraints and HTTP's infrastructure (caches, proxies, load balancers) works with you rather than against you.


Authentication vs Authorisation — two different questions

Authentication answers "who are you?" Authorisation answers "what are you allowed to do?" They're related but distinct — and systems that conflate them create security vulnerabilities that are subtle and hard to audit.

A user who authenticates successfully is identified. Whether they're authorised to access a specific resource is a separate decision, checked separately, enforced separately. Getting this separation wrong leads to patterns like "any authenticated user can access any resource" — which is not a security model, it's a security incident waiting to be filed.

Best mental model: authentication is showing your passport at the border — it establishes your identity. Authorisation is the visa — it establishes what you're permitted to do once inside. You can have a valid passport and be refused entry. You can have a visa that permits some activities and prohibits others. The two checks happen in sequence but serve completely different purposes.


Session vs Token Authentication — stateful vs stateless identity

Session-based authentication stores identity state on the server — a session record maps a session ID (stored in a cookie) to a user. Token-based authentication encodes identity in the token itself — the server validates the token cryptographically without storing any session state.

Session auth is simple and easy to revoke. Token auth scales horizontally without shared session storage, works across domains, and fits naturally with mobile and single-page applications. The tradeoffs are real in both directions and the right choice depends on your deployment model.

Best mental model: session auth is a coat check — you hand in your coat, get a numbered ticket, and the cloakroom remembers which coat is yours. Token auth is a signed membership card — it contains all your details, the venue verifies the signature, and no lookup is required. The coat check is easy to cancel (throw away the record). The membership card keeps working until it expires, even if the venue wishes it didn't.


OAuth 2.0 & OpenID Connect — delegated access and federated identity

OAuth 2.0 is the industry-standard protocol for delegated authorisation — allowing a user to grant a third-party application access to their resources without sharing their credentials. OpenID Connect (OIDC) extends OAuth 2.0 with an identity layer, enabling "Sign in with Google/GitHub/Apple."

These protocols are the infrastructure behind every "Login with..." button and every "Connect your account" integration. Understanding them properly — not just copying a library's example code — is what lets you reason about the security properties of your authentication system and identify when an integration is implemented incorrectly.

Best mental model: OAuth is like a hotel key card system. The hotel (authorisation server) issues a key card (access token) that opens specific doors (resources) for a limited time. The hotel doesn't give you a master key — it gives you access to exactly what you need. If you lose the card, it stops working at expiry, or the hotel can deactivate it remotely.


JWT — the token format underlying most modern auth

JWT (JSON Web Token) is a compact, self-contained token format used across OAuth, OIDC, and many bespoke authentication systems. A JWT encodes claims — assertions about the user or session — in a JSON payload, signs it cryptographically, and base64-encodes the result into a string that can be passed in HTTP headers.

JWTs are everywhere. They're also frequently misused — unsigned tokens accepted as trusted, sensitive data stored in unencrypted payloads, expiry times set to years. Understanding exactly what a JWT guarantees — and what it doesn't — is essential for any engineer building or reviewing authentication systems.

Best mental model: a JWT is a sealed letter of introduction. The seal (signature) proves it hasn't been tampered with since it was written. The contents (claims) are readable by anyone — the seal doesn't encrypt, it authenticates. Don't put secrets in a JWT any more than you'd write passwords in a letter of introduction.


WebSockets — bidirectional real-time communication

HTTP is request/response — the client asks, the server answers. WebSockets upgrade that TCP connection into a persistent, bidirectional channel where either side can send messages at any time, without a new request.

This changes what's possible: real-time collaborative editing, live dashboards, multiplayer games, instant messaging — all of these require the server to push data to the client without waiting for a request. WebSockets make that native and efficient.

Best mental model: HTTP is a walkie-talkie — one person talks, presses the button, the other responds. WebSockets are a phone call — both parties are connected and can speak at any moment. The walkie-talkie is fine for most communication; the phone call is essential when you need real-time, spontaneous exchange.


Long Polling, Server-Sent Events & Webhooks — the server-push spectrum

Before WebSockets were universally supported, and still today for simpler use cases, three patterns handle "the server needs to tell the client something happened":

Long polling — the client asks, the server holds the connection open until it has something to say, then responds. The client immediately asks again. Simulates push with repeated pull.

Server-Sent Events (SSE) — a single HTTP connection the server streams data down indefinitely. One direction only (server to client), but simple, robust, and natively supported by browsers.

Webhooks — the server makes an HTTP request to the client when an event occurs. The client must expose an endpoint. No persistent connection — the push is a regular outbound HTTP call.

Each is right for different circumstances, and knowing which to reach for prevents over-engineering.

Best mental model: long polling is checking your letterbox every 30 seconds. SSE is postal delivery — the postman arrives when there's mail. Webhooks are a courier who rings your doorbell when a package arrives — but you need to be home to answer.


Synchronous vs Asynchronous Communication — the architectural fork

Synchronous communication means the caller waits for a response before proceeding. Asynchronous means the caller fires a request and continues — the response (if any) arrives later, handled separately.

This choice reverberates through an architecture. Synchronous systems are simpler to reason about but couple services together — if the downstream service is slow or unavailable, the upstream service stalls. Asynchronous systems decouple services and absorb traffic spikes, but introduce new complexity: message ordering, delivery guarantees, idempotency, and eventual consistency.

Best mental model: synchronous is asking a colleague a question and waiting at their desk for an answer. Asynchronous is sending them an email — you write it, send it, and go back to work. The answer arrives when it arrives. Both are legitimate; the choice depends on whether you can afford to wait.


API Gateways — the traffic management layer

An API gateway is a reverse proxy that sits in front of your services and handles cross-cutting concerns: authentication, rate limiting, routing, request transformation, logging, and monitoring — all in one place, before requests reach your application code.

Without an API gateway, every service reimplements authentication, every service has its own rate limiting logic, and every service has different observability. With one, those concerns are centralised, consistent, and configurable without touching service code.

Best mental model: an API gateway is airport security — a single mandatory checkpoint that handles identity verification, item inspection, and routing to the right gate, regardless of which airline you're flying. The airlines don't each run their own security; they rely on a centralised system that applies consistent standards to everyone.


The running example: URL shortener as a platform

This pillar extends the URL shortener into a platform with multiple consumers:

  • A public REST API for third-party developers creating and managing short links
  • JWT-based authentication for the web and mobile apps
  • OAuth 2.0 for "Login with Google" on the web app
  • Webhooks notifying partners when new links are created
  • WebSockets powering the real-time analytics dashboard
  • An API gateway managing authentication, rate limiting, and routing for all of the above

Each concept in this pillar will be grounded in a concrete decision this platform needs to make.


The decision framework

What kind of interaction is this?

Request/response, client initiates?
  └─ REST API — is the interaction CRUD on a resource?
      └─ Yes → REST with appropriate HTTP method and status code
      └─ No  → Consider RPC-style endpoint or GraphQL

Does the server need to push data to the client?
  └─ Bidirectional, real-time? → WebSockets
  └─ Server-to-client stream?  → Server-Sent Events
  └─ Occasional push, client has endpoint? → Webhooks
  └─ No persistent connection needed? → Long polling

Does the caller need to wait for the result?
  └─ Yes, and latency is acceptable → Synchronous
  └─ No, or result is delayed → Asynchronous (message queue)

Who is the caller?

External developer?
  └─ API gateway → authentication → rate limiting → routing

User of your application?
  └─ Need to know who they are? → Authentication (session or token)
  └─ Need to control what they access? → Authorisation (RBAC, scopes)
  └─ Third-party login? → OAuth 2.0 / OpenID Connect
  └─ Passing identity between services? → JWT

Common traps at this stage

Conflating authentication and authorisation. Checking that a user is logged in is not the same as checking that they're allowed to access a specific resource. Every endpoint needs both checks, independently.

Treating REST as just "HTTP with JSON." REST has specific constraints — statelessness, resource-orientation, uniform interface — that are often violated by APIs that call themselves REST. Understanding which constraints actually matter for your use case prevents both cargo-culting and throwing away genuinely useful conventions.

Using WebSockets for everything real-time. WebSockets are powerful but operationally heavier than SSE for unidirectional server push. SSE is often the right tool for dashboards, notifications, and live feeds — simpler to implement, naturally compatible with HTTP/2 multiplexing, and easier to debug.

JWT as a session replacement with no revocation strategy. JWTs are stateless — the server can't invalidate them before expiry. Using long-lived JWTs without a revocation mechanism means a compromised token is valid until it naturally expires. Short expiry times combined with refresh tokens are the standard mitigation.

Building cross-cutting concerns into every service. Authentication, rate limiting, and logging implemented separately in every service creates drift and maintenance burden. An API gateway centralises these concerns — but only if teams actually use it rather than reimplementing in-service "just this once."


Key takeaways

  • API design is a contract — build for the consumer's mental model, not the implementation's convenience
  • REST is an architectural style with specific constraints, not just "HTTP with JSON"
  • Authentication is identity; authorisation is permission — separate concerns requiring separate implementations
  • Session auth stores state on the server; token auth encodes state in the token — different tradeoffs for different deployment models
  • OAuth 2.0 is the standard for delegated access; OpenID Connect adds identity on top
  • JWT is a signed, self-contained token — not encrypted by default, not revocable without additional infrastructure
  • WebSockets enable bidirectional real-time communication over a persistent connection
  • Long polling, SSE, and webhooks are the spectrum of server-push patterns — each right for different circumstances
  • Sync vs async is the architectural fork — synchronous couples services, asynchronous decouples them
  • API gateways centralise cross-cutting concerns — auth, rate limiting, routing, observability

Up next

Part 1 → API Design: Building Contracts That Last

We start with the principles that make an API good before any framework, protocol, or technology choice. Naming conventions, versioning strategies, error design, and the question of whether your API is designed for the consumer or the implementer.


Part of the System Design series. Tags: #systemdesign #api #distributedsystems #backend #softwarearchitecture #engineering

→ Next: API Design — principles for building APIs that last

Systems Design

Part 31 of 50

Understanding these system design concepts is essential for architects, developers, and engineers to create scalable, reliable, and maintainable software systems that meet the needs of businesses.

Up next

API Design: Building Contracts That Last

Systems Design # Post What it covers 00 APIs & Communication: How Services Talk to Each Other How services talk to each other shapes everything about a system. Nine concepts covering REST, WebSo