Choosing RPC, gRPC, or Event APIs for Service Integration

Compare REST-style RPC, gRPC, and event APIs by interaction pattern, latency, coupling, tooling, and operational needs before choosing an integration style.

published: reading time: 8 min read author: GeekWorkBench
Quick Summary

RPC, gRPC, and event APIs give services different ways to coordinate work. This guide compares request-response calls with asynchronous events, including their latency, coupling, contracts, tooling, and failure behavior. It explains how an outbox protects publication intent and why consumers still need deduplication and idempotent effects. Use the examples and trade-offs to choose an interaction style that fits your callers and the operations your team can support.

Choosing RPC, gRPC, or Event APIs for Service Integration

Introduction

Services can communicate through request-response calls or by publishing work and events for later processing. REST-style HTTP, gRPC, and event APIs make different trade-offs in latency, coupling, typing, delivery guarantees, and operational complexity.

Consider a checkout service that must tell a customer whether stock was reserved. That needs a request-response call. An OrderPlaced event can let analytics and fulfillment react after checkout without making the checkout wait for them.

The sections below compare when each approach fits. They also cover reliable event publication with an outbox, duplicate handling, observability, and the failure cases teams should plan for.

gRPC: typed RPC with generated clients

gRPC uses Protocol Buffers to define services and messages, then generates client and server code. It supports unary calls and streaming patterns, and its compact binary encoding can suit internal service traffic. The schema makes many changes visible during development, but teams need code generation, version compatibility practices, and tooling for debugging binary payloads. Browser use often requires gRPC-Web or a gateway, so it is not automatically the best public interface.

Events: publish that something happened

An event such as OrderPlaced records a fact that consumers can process independently. The producer does not need to know every consumer, and consumers can handle work asynchronously. Events are useful for notifications, fan-out, and workflows that should survive temporary consumer outages. They introduce eventual consistency, duplicate delivery, ordering questions, schema evolution, and more operational machinery. An event should state what happened, not pretend the producer can guarantee every subscriber completed work.

Reliable event publication with an outbox

An event-driven service often needs to update its database and publish an event for the same operation. If it commits the database change first and crashes before publishing, consumers never hear about the change. If it publishes first and the database transaction later rolls back, consumers see an event for work that did not happen. These are two sides of the dual-write problem.

A transactional outbox records the event in the same database transaction as the domain change. A separate publisher reads pending outbox rows and sends them to the broker, then marks them delivered. The publisher can send a row more than once if it crashes between publish and acknowledgement, so consumers still need an event ID, deduplication, and idempotent effects. The outbox closes the gap between the service’s database write and its publication intent; it does not create exactly-once delivery across the whole system.

flowchart TD
    A[Need an interaction] --> B{Must caller wait for result?}
    B -->|Yes| C{Typed internal service contract?}
    C -->|Yes| D[gRPC or typed RPC]
    C -->|No| E[HTTP JSON request-response]
    B -->|No| F[Publish event or command]
    F --> G[Consumers process independently]

When to use and when not to use

Use HTTP/JSON for public APIs, browser clients, and integrations where broad tool support matters. Use gRPC when services need strongly typed contracts, efficient internal calls, or streaming and your stack supports the tooling. Use events for decoupled asynchronous work and fan-out. Avoid choosing gRPC solely because binary sounds faster, or events solely to avoid designing a synchronous contract. For a simple operation requiring an immediate answer, events can make the product flow harder than it needs to be.

Trade-off analysis

Style Strength Cost
HTTP/JSON RPC or REST Broad tooling and easy inspection Larger payloads; contract discipline still needed
gRPC Generated types and efficient streaming calls Codegen, gateway, and debugging requirements
Event API Decoupled consumers and async processing Eventual consistency, duplicates, and broker operations
Synchronous call chain Immediate result Availability and latency compound across dependencies

Implementation snippet

A typed service definition makes the RPC boundary explicit. A JSON event should also have a stable name and schema version:

syntax = "proto3";
service Inventory {
  rpc Reserve (ReserveRequest) returns (ReserveReply);
}
message ReserveRequest { string sku = 1; int32 quantity = 2; string request_id = 3; }
message ReserveReply { bool reserved = 1; string reservation_id = 2; }

For an event path, include a unique event ID, occurrence time, aggregate ID, and schema version. Consumers should deduplicate by event ID and make processing idempotent. Do not use a request ID as a substitute for a durable event identity.

Production failure scenarios and mitigations

A synchronous chain of five services exceeds the caller’s deadline; set per-hop budgets and reduce unnecessary hops. A gRPC schema renumbers a field that was previously shipped; never reuse field numbers and reserve removed ones. A broker delivers an event twice; deduplicate and make the handler idempotent. Consumers process related events out of order; partition by aggregate key or include sequence numbers and define handling. A public browser cannot call native gRPC; expose a compatible HTTP gateway or use a different contract.

Observability checklist

  • For RPC: record method, status, deadline exceeded, and dependency latency with trace context.
  • For gRPC: monitor call status codes, stream duration, and generated client versions.
  • For events: track publish failures, queue lag, delivery attempts, dead-letter counts, and consumer age.
  • Correlate request and event IDs without logging message secrets or personal data.

Security notes and pitfalls

Authenticate and authorize every RPC; internal network location is not identity. Protect brokers with scoped producer and consumer permissions, encrypt transport, and validate message schemas at boundaries. Events can contain personal data that outlives the source record, so define retention and deletion behavior. Common pitfalls include assuming exactly-once delivery, unbounded retries, incompatible schema changes, hidden synchronous calls inside event consumers, and overlooking browser or proxy support.

Quick Recap Checklist

  • Choose synchronous RPC when the caller needs the result before continuing.
  • Choose events when producers and consumers can progress independently and eventual consistency is acceptable.
  • Confirm client environments support the chosen transport and generated-code workflow.
  • Define timeout, retry, schema evolution, and observability policies for the integration.
  • Assume event delivery can repeat and make consumer effects idempotent or deduplicated.

Interview Questions

1. When would you choose an event API over a synchronous RPC?
Choose events when the producer should not wait for consumers, several consumers need independent processing, or temporary consumer outages should not block the original operation. The caller must be able to tolerate eventual consistency.
2. What does gRPC provide beyond HTTP with JSON?
It provides schema-defined messages, generated clients and servers, compact Protobuf encoding, and streaming RPC patterns. Those benefits come with code generation, compatibility, and tooling requirements.
3. Why is exactly-once event processing difficult to guarantee?
A broker and consumer can fail between committing work and acknowledging delivery. Systems commonly provide at-least-once delivery, so consumers use deduplication and idempotent effects to make repeats safe.
4. How does a transactional outbox prevent a lost event after a database update?
The service writes the domain change and an outbox row in the same transaction. A separate publisher sends pending rows to the broker; consumers still need to tolerate duplicate delivery.
5. How can a consumer preserve ordering for events about one aggregate?
Partition related events by a stable aggregate key and include sequence information if consumers need to detect gaps or reordering. Ordering is usually scoped to a partition, not guaranteed across the entire broker.
6. What should a consumer do with a message that repeatedly fails processing?
Use a bounded retry policy and move messages that exceed it to a dead-letter queue with enough safe metadata to investigate and replay them. Unbounded retries can block progress or amplify an outage.
7. When is gRPC streaming useful compared with unary RPC?
Use unary calls for one request and one response. Streaming fits a sequence of related messages or a long-lived flow where either side needs to send updates without opening a new call for every item.
8. How does an event differ from a command message?
An event states that something has already happened, such as OrderPlaced. A command asks a recipient to do something, such as ReserveInventory, and may be accepted or rejected.

Further Reading

Conclusion

Choose the interaction first, then choose the interface. Synchronous APIs fit work that needs an answer; event APIs fit work that can continue independently, provided the team plans for eventual consistency and duplicate delivery.

Category

Related Posts

API Clients, Servers, and Network Boundaries Explained

Understand what API clients and servers each own, how network boundaries fail, and how timeouts, retries, and trust boundaries shape reliable integrations.

#api-design #networking #distributed-systems

Idempotency, Deduplication, and Safe Replays

Design idempotent API operations and deduplication records so clients can retry after timeouts without creating duplicate payments, jobs, or updates.

#api-design #idempotency #retries

Partial Failure, Ordering, and Eventual Consistency

Understand partial API failures, message ordering, and eventual consistency, then design status models and recovery paths clients can reason about.

#api-design #distributed-systems #consistency