API Gateways and Service Communication: REST, gRPC, and Events
- system-design
- distributed-systems
- architecture
- microservices
- backend
Once a system grows past a single service, two questions appear that a monolith never had to answer. How does the outside world talk to your fleet of services, and how do those services talk to each other? The first question is usually answered by an API gateway. The second is answered by a choice between a few communication styles, each with a personality of its own. Getting both right is a lot of what separates a distributed system that feels coherent from one that feels like a pile of services duct-taped together.
This covers the gateway's role and the main options for service-to-service communication. It connects to the concrete NestJS microservices, gRPC services, and event-driven implementations if you want to see the ideas in code.
What an API gateway does
An API gateway is a single entry point that sits in front of your services and handles the concerns that every service would otherwise have to implement itself. Clients talk to the gateway; the gateway routes each request to the right service and handles the cross-cutting work along the way.
The value is consolidation. Instead of every service reimplementing the same edge concerns, you do them once at the gateway:
- Routing. Map incoming paths to the services that handle them, so clients see one coherent API rather than a dozen separate service addresses.
- Authentication. Verify the caller's identity once at the edge, then pass a trusted identity inward, so individual services do not each re-authenticate.
- Rate limiting. Enforce rate limits at the door, protecting every service behind it from abuse in one place.
- TLS termination, request aggregation, and response shaping. Handle HTTPS centrally, and optionally combine several service calls into one client response.
The gateway is also where you hide your internal structure. Clients do not need to know you split a feature across three services; they see one endpoint, and you can reorganize behind the gateway without breaking them. That decoupling is a large part of the point.
The trade-off to respect: the gateway is on the critical path for every request, so it is both a potential bottleneck and a single point of failure. It has to be kept lightweight and run redundantly, or it becomes the thing that takes the whole system down. A gateway that does too much — heavy business logic, slow transformations — quietly re-centralizes the coupling you split services to avoid.
Synchronous communication: REST and gRPC
When one service needs something from another and needs it now — the caller waits for the response before continuing — that is synchronous communication. Two dominant styles.
REST over HTTP is the default for good reason. It is universally understood, works with every language and tool, is easy to debug with ordinary HTTP tooling, and is human-readable over the wire with JSON. For most service-to-service calls, and for anything a browser or third party will consume, REST is the sensible, boring, correct choice. Its costs are that JSON is verbose and text-based, and that REST has no built-in contract enforcement, so drift between what a service returns and what its callers expect is easy.
gRPC trades universality for performance and rigor. It uses Protocol Buffers, a compact binary format, over HTTP/2, which makes it markedly faster and lighter than JSON over HTTP/1.1, and it defines a strict contract in a schema that both sides generate code from, so the client and server cannot silently disagree about the shape of a message. It also supports streaming naturally. The costs are that it is not human-readable, browsers cannot call it directly without a proxy, and the tooling is heavier. gRPC shines for high-volume internal service-to-service traffic where the performance and the enforced contract genuinely pay off; it is usually the wrong choice for a public-facing API.
A reasonable default: REST at the edge where compatibility matters, gRPC between internal services where throughput and contract safety matter.
Asynchronous communication: events
Synchronous calls have a hidden cost that grows with your system: coupling in both time and availability. If service A calls service B synchronously, A is blocked until B responds, and if B is down, A's request fails. Chain a few of those together and one slow service degrades everything upstream of it. The more services call each other synchronously, the more fragile and interconnected the whole system becomes.
Asynchronous, event-driven communication breaks that coupling. Instead of calling another service directly, a service publishes an event — "order placed" — to a message queue or stream, and moves on. Other services subscribe and react in their own time. The publisher does not know or care who is listening, and it does not wait. If a consumer is down, the event waits in the queue until it recovers; nothing upstream fails.
This is transformative for resilience and decoupling. The order service does not need to know that billing, notifications, and analytics all care about a new order. It announces the fact and continues. New consumers can be added later without touching the publisher. The cost is that you give up the simplicity of an immediate answer: the work happens eventually, not now, and you inherit the duplicate delivery and idempotency concerns that come with any queue. You also lose the easy mental model of a straight-line call stack, trading it for a system whose behavior is spread across many independent reactions.
Choosing between them
The question to ask is whether the caller genuinely needs the answer before it can continue.
- If it does — a checkout that must confirm payment before showing success — use synchronous communication. REST for reach and simplicity, gRPC for internal performance and contract enforcement.
- If it does not — sending a receipt, updating analytics, notifying other systems — use asynchronous events. The caller should not wait for work that only needs to happen eventually, and it should not fail because an unrelated downstream is down.
Most real systems are a blend. The user-facing request path leans synchronous because users are waiting for answers, while the ripple effects of each action fan out asynchronously through events. Drawing that line well — synchronous where the answer is needed now, asynchronous everywhere else — is one of the most consequential decisions in a distributed design, and one the system design framework pushes you to make explicitly rather than by accident.
Key takeaways
- An API gateway is a single entry point that centralizes routing, authentication, rate limiting, and TLS so services do not each reimplement edge concerns.
- The gateway is on every request's critical path, so keep it lightweight and redundant or it becomes a bottleneck and single point of failure.
- REST is the universal, debuggable default; gRPC is faster and contract-enforced via Protocol Buffers, best for high-volume internal traffic.
- Synchronous calls couple services in time and availability; a slow or down service degrades everything calling it.
- Event-driven async communication decouples services — publish an event and move on — at the cost of eventual processing and idempotency concerns.
Frequently asked questions
What does an API gateway do?
It provides a single entry point in front of your services and handles cross-cutting concerns once: routing requests to the right service, authenticating callers at the edge, rate limiting, TLS termination, and sometimes aggregating responses. It also hides your internal structure so you can reorganize services without breaking clients.
When should I use gRPC instead of REST?
Use gRPC for high-volume internal service-to-service traffic where its binary Protocol Buffers format, HTTP/2 performance, and strict generated contracts pay off. Use REST at the edge and for anything public or browser-facing, where universal compatibility and human-readable debugging matter more than raw throughput.
What is the difference between synchronous and asynchronous service communication?
Synchronous means the caller waits for a response before continuing (REST, gRPC) and fails if the callee is down. Asynchronous means the caller publishes an event to a queue and moves on, while other services react in their own time — which decouples services and absorbs downstream outages, at the cost of eventual rather than immediate processing.
Should services communicate synchronously or asynchronously?
Ask whether the caller genuinely needs the answer before it can continue. If it does, like confirming payment at checkout, use synchronous. If it does not, like sending a receipt or updating analytics, use asynchronous events so the caller does not wait or fail because of an unrelated downstream. Most systems blend both.
Further reading
Explore more on
Free tools for this topic

About the author
I'm Aman Kumar Singh, a software engineer in Noida, India building scalable full-stack products with React, Next.js, Node.js, NestJS, PostgreSQL, Redis, and AWS. I write about backend engineering, distributed systems, and system design.