Communication Protocols Between Services
Modern backends rarely speak one language. Clients hit REST endpoints through an API Gateway. Mobile apps send GraphQL queries to fetch exactly the fields they need. Dashboards maintain WebSocket connections for live updates. External partners push events via webhooks. Inside the backend, services call each other with gRPC — and legacy systems still expect SOAP.
Each protocol solves a different problem at a different layer. Understanding where each one belongs — client-facing vs internal, synchronous vs callback-driven — is what keeps a distributed architecture maintainable instead of chaotic.
The Protocol Stack: Clients to Backend
A typical microservices architecture separates protocols by layer. At the edge, clients interact through REST, GraphQL, or WebSockets. An API Gateway sits in front of most HTTP traffic — routing requests, enforcing auth, rate limits, and SSL termination. GraphQL often lives alongside or behind the gateway, letting clients request shaped data without multiple round trips.
Inside the backend boundary, services communicate with faster, typed protocols like gRPC. Legacy systems that predate REST may still require SOAP adapters. External systems push data in through webhooks — HTTP callbacks triggered when something happens on their side. No single protocol handles all of these roles well.
- •Edge layer: REST, GraphQL, WebSocket — optimized for client diversity.
- •Gateway layer: API Gateway + optional GraphQL server — single entry point.
- •Internal layer: gRPC for service-to-service, SOAP for legacy adapters.
- •Integration layer: Webhooks for external system callbacks.
- •Pick the protocol based on who is calling and what they need — not personal preference.
Takeaway: Clients use REST/GraphQL/WebSocket at the edge; services use gRPC internally; legacy systems get SOAP adapters.
REST via API Gateway
REST remains the default client-facing protocol. Clients request resources by URL — GET /entityA/42, POST /entityB — and receive JSON responses. The API Gateway receives these requests and routes them to the correct internal service without exposing service topology to the client.
This indirection matters. You can split a monolithic /users endpoint across three microservices behind the gateway without changing the client contract. The gateway also centralizes cross-cutting concerns: authentication tokens, request logging, CORS headers, and rate limiting happen once at the edge instead of in every service.
- •Resource URLs: /entityA/{id}, /entityB — nouns, not verbs.
- •Gateway handles auth, rate limiting, routing, and SSL.
- •Version URLs (/v1/) so internal services can evolve independently.
- •Pair with OpenAPI specs so client and server teams share one contract.
- •Always set timeouts on outbound gateway-to-service calls.
Takeaway: REST through an API Gateway gives clients one front door while services stay hidden behind it.
GraphQL for Flexible Client Queries
GraphQL lets clients request exactly the data they need in a single round trip. Instead of calling /entityA and /entityB separately, the client sends one query: { entityA { message } }. A GraphQL server resolves fields by calling the appropriate backend services — often routing through the same API Gateway for auth and policy enforcement.
GraphQL shines when different clients need different shapes of the same data. A mobile app might request id and title; a web dashboard might request id, title, author, comments, and metadata. One endpoint serves both without over-fetching or under-fetching. The trade-off is complexity: query validation, N+1 resolver problems, and caching are harder than with plain REST.
- •Best for: mobile apps, complex dashboards, multiple client types.
- •GraphQL server resolves fields → calls internal REST or gRPC services.
- •Use DataLoader to batch N+1 resolver calls into single backend requests.
- •Disable introspection in production; limit query depth and complexity.
- •Not a replacement for REST — use both where each fits.
Takeaway: GraphQL gives clients one endpoint and full control over response shape — at the cost of server complexity.
WebSockets for Real-Time Push
Unlike REST and GraphQL, WebSocket connections often bypass the API Gateway and connect directly to a backend service. After an HTTP upgrade handshake, the connection stays open and both sides can push messages at any time — no new request per update.
This makes WebSockets ideal for live dashboards, chat, notifications, and collaborative editing. The server pushes stock prices, order status changes, or chat messages the moment they happen. Operating WebSockets at scale requires sticky sessions or a shared pub/sub backplane (Redis, Kafka) so every server instance can reach every connected client.
- •Persistent bidirectional channel — either side sends at any time.
- •Often connects directly to backend, not through REST gateway.
- •Use WSS (WebSocket Secure) in production — same TLS as HTTPS.
- •Heartbeats (ping/pong) detect dead connections and free server memory.
- •Fall back to Server-Sent Events (SSE) for one-way server push when simpler.
Takeaway: WebSockets are for live, push-based updates — not for standard CRUD request-response.
Webhooks from External Systems
Webhooks flip the request direction. Instead of your backend polling an external system for changes, the external system sends an HTTP POST to your endpoint when something happens — a payment completes, a shipment ships, a user signs up on a partner platform.
Webhook handlers must be idempotent and fast. The external system expects a 200 response quickly; heavy processing should be offloaded to a queue. Always verify webhook signatures (HMAC, shared secret) so attackers cannot forge events. Store raw payloads before processing so you can replay failed events.
- •External system pushes HTTP POST to your URL on each event.
- •Respond 200 quickly; process asynchronously via queue.
- •Verify signatures (Stripe, GitHub, Shopify all provide HMAC verification).
- •Make handlers idempotent — the same event may be delivered twice.
- •Log raw payloads for debugging and replay of failed processing.
Takeaway: Webhooks let external systems notify you in real time — verify signatures and process asynchronously.
gRPC for Internal Service Calls
Inside the backend, services talk to each other with gRPC — not REST. Service A calls Service B with a binary Protobuf message over HTTP/2. Service B forwards to Service S the same way. This is faster and more type-safe than JSON REST between services.
.proto files define the contract. Code generators produce typed stubs in Go, Java, C#, Python, and more — compile-time errors instead of runtime JSON parsing failures. gRPC supports unary calls, server streaming, client streaming, and bidirectional streaming. It is not browser-friendly without a gRPC-Web proxy, which is why it stays internal.
- •Service A → Service B → Service S via gRPC, not REST.
- •Define contracts in .proto; generate stubs for each language.
- •Unary for request-response; streaming for large datasets.
- •Use grpcurl or server reflection for debugging binary payloads.
- •Keep gRPC internal — expose REST/GraphQL at the gateway for clients.
Takeaway: gRPC is the internal highway — typed, fast, binary — kept behind the gateway from external clients.
SOAP for Legacy Integration
Not every system in your backend was built this decade. Enterprise legacy services — mainframes, old ERP systems, government APIs — often expose SOAP endpoints with XML envelopes and WSDL contracts. Service H acts as a modern adapter: it accepts gRPC or REST internally and translates to SOAP when calling the legacy service.
The Anti-Corruption Layer pattern fits here. Wrap the legacy SOAP API behind a clean internal interface so the rest of your services never touch XML or WSDL directly. When the legacy system is eventually replaced, you swap the adapter — nothing else changes.
- •SOAP: XML envelopes, WSDL contracts, common in enterprise and legacy systems.
- •Service H adapts modern internal calls → SOAP for the legacy service.
- •Use Anti-Corruption Layer — never let SOAP/XML leak into domain logic.
- •Generate clients from WSDL (wsdl2java, svcutil) rather than hand-writing XML.
- •Plan migration: adapter makes legacy swappable without touching callers.
Takeaway: Wrap legacy SOAP behind a modern adapter — the rest of your system should never speak XML directly.
Which Protocol Goes Where?
The diagram shows the answer: protocol choice follows the boundary. Clients use REST or GraphQL through the API Gateway. Real-time dashboards use WebSockets directly to the backend. External partners push via webhooks. Internal services call each other with gRPC. Legacy systems get a SOAP adapter.
Trying to use one protocol everywhere creates friction — forcing gRPC on browser clients, or REST between high-throughput internal services, or polling instead of webhooks. Match the protocol to the caller, the data shape, and the latency requirement.
- •Browser/mobile CRUD → REST via API Gateway.
- •Multiple client types, shaped responses → GraphQL.
- •Live push (chat, dashboards, notifications) → WebSocket.
- •External system notifies you → Webhook with signature verification.
- •Internal service-to-service, high throughput → gRPC.
- •Legacy enterprise system → SOAP adapter (Anti-Corruption Layer).
Takeaway: Protocol follows the boundary — edge for clients, gRPC internally, SOAP for legacy, webhooks for external push.
Final Thoughts
A production backend is a protocol sandwich. REST and GraphQL serve clients at the edge. WebSockets push live updates. Webhooks receive external events. gRPC connects services inside the backend. SOAP adapters bridge legacy systems without polluting modern code.
You do not need all of them on day one. Start with REST through an API Gateway and direct service calls. Add gRPC when internal latency matters. Add GraphQL when client data shapes diverge. Add WebSockets when users need live data. Add webhooks when partners push events. Wrap legacy SOAP only when you must. Each protocol earns its place at a specific boundary.