Framing the Decision
gRPC gives you four RPC shapes — unary, server streaming, client streaming, and bidirectional streaming — and the right choice comes down to two questions: does either side need to send more than one logical message per call, and does the timing of those messages matter, meaning do they need to be processed as they arrive rather than only once the whole set is complete? Answering those two questions for a given API method almost always narrows the choice to exactly one of the four patterns, rather than leaving it a matter of taste.
Cricket analogy: It is like a captain deciding a bowling change: is this a single over (unary), a spell that needs continuous field adjustments (streaming), or a full tactical back-and-forth with the coach over the radio (bidirectional) — the situation dictates the shape, not preference.
A Practical Decision Framework
Start with unary as the default: if the client sends one thing and needs one answer, stop there — it is the simplest to implement, retry, and load-balance. Move to server streaming only if the server's answer is naturally a sequence the client should start consuming before it's all ready, such as search results or a live feed. Move to client streaming only if the client is producing a sequence and the server only needs to react once, at the end. Reach for bidirectional streaming only when both sides genuinely need to react to each other's messages independently over time, since it is the most complex pattern to implement correctly, particularly around concurrency, backpressure, and reconnection.
Cricket analogy: It is like a team management default of picking your first-choice XI unless conditions clearly demand a specialist — you only bring in the four-pronged pace attack (bidirectional-level complexity) when the pitch genuinely requires it.
Comparing the Four Patterns
service ExampleService {
// Unary: one in, one out.
rpc GetUser(GetUserRequest) returns (GetUserResponse);
// Server streaming: one in, many out.
rpc WatchOrders(WatchOrdersRequest) returns (stream OrderEvent);
// Client streaming: many in, one out.
rpc UploadLogs(stream LogLine) returns (UploadSummary);
// Bidirectional streaming: many in, many out, independently.
rpc Chat(stream ChatMessage) returns (stream ChatMessage);
}Operationally, the four patterns also differ in how they interact with infrastructure: unary calls are the easiest to safely retry, cache, and load-balance per request because each call is self-contained; streaming calls, by contrast, pin a client to one specific server instance for the lifetime of the stream, which matters for connection draining during deployments and for how your load balancer or service mesh handles long-lived streams versus short unary requests. Teams migrating from a REST-and-polling architecture often start by converting the highest-frequency polling endpoint to server streaming, since that single change usually removes the most redundant request traffic for the least implementation complexity.
Cricket analogy: It is like a broadcaster's decision to keep the same commentary pair on air for an entire session (a streaming pin) versus rotating a different expert in for each individual replay analysis (a unary call), each with different production logistics.
Do not default to bidirectional streaming 'to be safe' or 'future-proof'. It is genuinely harder to reason about (concurrency, ordering, reconnection, load balancing of long-lived connections) than the other three patterns, and using it where a simpler pattern would do adds real operational cost for no benefit.
- Choose the RPC type by asking whether each side sends one or many messages, and whether timing matters.
- Unary is the correct default: simplest to retry, cache, and load-balance per request.
- Server streaming fits a naturally sequential server response the client should consume incrementally.
- Client streaming fits a client-produced sequence needing only one final server reaction.
- Bidirectional streaming is reserved for cases where both sides must react to each other continuously.
- Streaming calls pin a client to one server instance for the stream's lifetime, affecting load balancing and deployments.
- Avoid defaulting to bidirectional streaming for its own sake — it adds real complexity without benefit if unneeded.
Practice what you learned
1. According to the decision framework, what should be the default RPC type unless a specific need dictates otherwise?
2. Which RPC type fits a scenario where the client uploads many log lines and only needs one final acknowledgment?
3. Why do streaming RPCs affect load balancing differently than unary RPCs?
4. What is a common risk the material warns against when choosing an RPC type?
Was this page helpful?
You May Also Like
Unary RPC Explained
The simplest gRPC call pattern: one request in, one response out, modeled as a single HTTP/2 stream that opens and closes in a single round trip.
Server Streaming RPC
A gRPC pattern where the client sends one request and the server replies with a sequence of messages over time on the same stream.
Client Streaming RPC
A gRPC pattern where the client sends a sequence of messages over one stream and the server replies with a single response once the client finishes.
Bidirectional Streaming RPC
A gRPC pattern where client and server each send an independent, ordered stream of messages over the same connection at the same time.
Related Reading
Related Study Notes in Web Development
Browse all study notesWebSockets Study Notes
Web Development · 30 topics
Web DevelopmentWebAssembly Study Notes
WebAssembly · 30 topics
Web DevelopmentSpring Boot Study Notes
Java · 30 topics
Web DevelopmentFlask Study Notes
Python · 30 topics
Web DevelopmentDjango Study Notes
Python · 30 topics
Web DevelopmentNext.js Study Notes
JavaScript · 30 topics