The Four Streaming Modes

gRPC — The Four Streaming Modes
gRPC
Chapter 4 · The Four Streaming Modes

🔀 The Four Streaming Modes

Chapter 3 covered unary calls — one request, one response. HTTP/2's native support for long-lived, multiplexed streams (Chapter 1) lets gRPC go further, with three more call shapes REST has no built-in equivalent for at all (REST needs WebSockets or SSE bolted on, already covered in the WebSockets course).

Unary — Recap

One request in, one response out. The baseline every other mode is described relative to.

rpc GetProduct(GetProductRequest) returns (Product);

Server Streaming

One request, many responses streamed back over time. The stream keyword on the response type marks it.

rpc WatchPrice(WatchPriceRequest) returns (stream PriceUpdate);

Right for live updates — subscribing to stock price changes, streaming search results as they're found, watching a long-running job's progress. One request establishes the subscription; the server pushes as many responses as it needs to, whenever it has something new.

Client Streaming

Many requests streamed from the client, one response at the end. The stream keyword moves to the request type instead.

rpc UploadReadings(stream SensorReading) returns (UploadSummary);

Right for batch uploads — sending a large file in chunks, or submitting many sensor readings and getting back a single confirmation/summary once all of them have arrived, rather than paying the overhead of a separate unary call per chunk.

Bidirectional Streaming

Both sides stream independently and simultaneously — neither side waits for the other to finish before sending more.

rpc Chat(stream ChatMessage) returns (stream ChatMessage);

Right for genuinely chat-like exchanges — a live chat, real-time collaborative editing — the closest gRPC gets to the full-duplex model web-sockets1-2 defined for raw WebSockets.

ModeSyntax ShapeUse Case
Unaryrpc M(Req) returns (Res);A single request/response — the default choice
Server streamingrpc M(Req) returns (stream Res);Live updates, subscriptions
Client streamingrpc M(stream Req) returns (Res);Batch uploads, chunked submission
Bidirectional streamingrpc M(stream Req) returns (stream Res);Chat, real-time collaboration

Implementing Server Streaming

function watchPrice(call) { const interval = setInterval(() => { call.write({ price: getCurrentPrice() }); }, 1000); call.on('cancelled', () => clearInterval(interval)); }

Unlike a unary handler's single callback, a server-streaming handler calls write() as many times as it needs to, and must explicitly handle the client disconnecting mid-stream — a lifecycle unary calls never have to think about.

gRPC Bidi Streaming vs. Raw WebSockets

Both are full-duplex, but gRPC's bidirectional streams are still schema-constrained by Protobuf messages and shaped around an RPC method — raw WebSockets (the WebSockets course) are a free-form byte/message channel with no inherent request/response or schema structure at all.

When to Reach for Which

gRPC bidi streaming fits when both ends are internal services that benefit from a typed schema; raw WebSockets fit better when a browser is one of the endpoints, or when the message shape needs to stay flexible.

💻 Coding Challenges

Challenge 1: Pick the Right Streaming Mode

For each, name the correct mode and write its rpc signature: (a) a client uploading a large log file in 1MB chunks, getting one confirmation back, (b) a dashboard subscribing to live order-count updates.

Goal: Practice matching a use case to the correct streaming direction and syntax.

→ Solution

Challenge 2: Explain Why Unary Doesn't Fit

Explain why implementing a live stock-price subscription as a unary call (the client polling repeatedly) is a worse fit than server streaming, in terms of both efficiency and design.

Goal: Practice articulating what server streaming specifically buys over repeated unary polling.

→ Solution

Challenge 3: gRPC Bidi or WebSockets?

A team is building a real-time multiplayer game where the browser is one of the two endpoints. Should they use gRPC bidirectional streaming or raw WebSockets? Justify your answer.

Goal: Practice applying this chapter's gRPC-vs-WebSockets distinction to a concrete architectural decision.

→ Solution

⚠️ Gotcha: Streaming Adds Real Complexity — Don't Reach for It by Default

A streaming handler has to manage a genuine lifecycle a unary call never does: knowing when the stream should end, handling a client disconnecting mid-stream, and dealing with errors that occur partway through rather than all-or-nothing. This complexity is worth paying when a use case genuinely needs ongoing or many-part communication — but it's a real cost, not a free upgrade. Unary should be the default for any call that's fundamentally "ask a question, get an answer"; reach for one of the three streaming modes only when the shape of the actual problem — live updates, chunked uploads, a genuinely two-way exchange — calls for it.

🎯 What's Next

The next chapter is Error Handling & Metadata — gRPC status codes vs. HTTP status codes, rich error details, metadata (gRPC's header equivalent), and deadlines/timeouts.