Lesson 7 · Streaming RPCs

Bidirectional streaming & choosing a type

Both directions at once — and the interview payoff: picking the right RPC type.

Your win: explain bidirectional streaming, and — the part interviews actually test — choose the right RPC type for any scenario in one sentence.

Bidi: two independent streams, one connection

Both sides stream, at the same time, independently. The client sends messages whenever it likes; the server sends whenever it likes; the two flows are decoupled (each is ordered within itself).1 The stream keyword is on both:

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

The Go handler is func(srv X_Server) error — and typically you run two goroutines: one looping srv.Recv(), one calling srv.Send() (your Go course concurrency, applied). This is the classic real-time chat / live collaboration shape.

Honest anchor: our repo has no bidi There is no bidirectional RPC anywhere in this monorepo. Notably, tom's chat uses server streaming (Subscribe, Lesson 5) to push events, plus separate unary calls to send messages — not one bidi stream. That's a deliberate design choice (simpler load balancing, reconnection, and auth per send) and a great thing to mention in an interview.

The decision guide (memorise this)

TypeChoose when…Repo example
Unaryone input → one output; the defaultall 3 services (SendEmail)
Server-streamone ask → many/open-ended results (a feed, push, progress)tom Subscribe
Client-streammany inputs → one result (upload, batch, aggregate)bob UploadAsset
Bidiinteractive, both sides send freely (real-time chat/collab)none

The rule: match the RPC type to the shape of the data flow — one/many on each side. Reach for streaming only when the data really is a sequence or open-ended; otherwise unary is simpler and easier to operate.

Why streaming costs more to operate

The trade-offs (interview gold) A long-lived stream pins a client to one server for its whole life — so it complicates load balancing (Part 5) and rolling deploys. Error handling is subtler (a failure mid-stream), deadlines apply to the whole stream, and you rely on HTTP/2 flow control for backpressure. Unary avoids all of this. "Prefer unary unless the data flow is genuinely a stream" is a strong answer.
Read this next

gRPC — Core concepts (Bidirectional streaming & RPC life cycle)

The authoritative description of all four types and their lifecycles side by side — the best single page for the "which type?" question.

grpc.io — bidirectional streaming

Check yourself (from memory)

Q1. In bidirectional streaming, the two streams are…

Each side sends when it likes; the flows are decoupled (ordered within each direction). Often two goroutines: one Recv, one Send.

Q2. Which type fits an interactive real-time chat best?

Both sides sending freely = bidi. (Though tom chose server-stream + unary sends — a valid, simpler-to-operate alternative.)

Q3. A long-lived stream complicates…

It pins a client to one server for the stream's life — awkward for LB and rolling deploys. Part 5 goes deeper.
Give the one-line decision rule for each of the four RPC types.
recall, then click to reveal
Unary — one request, one response (default). Server-streaming — one request, a stream of responses (feed / large result / progress; tom Subscribe). Client-streaming — a stream of requests, one response (upload / batch aggregate; bob UploadAsset). Bidirectional — independent streams both ways (interactive real-time; no repo example). Match the type to the data-flow shape, and remember long streams complicate deadlines and load balancing.
🎓 That's Part 2 — the four RPC types You can now read and choose among unary, server-, client-, and bidirectional streaming. Part 3 covers what wraps every call regardless of type: status codes, deadlines & cancellation, and metadata.
Ready for Part 3 (errors, deadlines & metadata), or a mixed quiz across Lessons 1–7 first? Ask me.

1. gRPC — Core concepts: bidirectional streaming.