Lesson 5 · Streaming RPCs

Server streaming

One request in, a stream of responses out — how live chat rides gRPC.

Your win: explain server streaming, read its Go handler shape (Send in a loop, no ctx arg), and say when to reach for it — anchored to tom's real-time chat Subscribe.

In plain English Plain English: server streaming means the client asks once and the server keeps sending multiple messages back over time.

The shape: 1 request → many responses

The client sends a single request; the server sends back a stream of messages, one at a time, until it closes the stream. The client reads them as they arrive.1 The stream keyword goes on the response:

proto/tom/v1/chat_modifier.proto:171
rpc Subscribe(SubscribeRequest) returns (stream SubscribeResponse);

The Go handler looks different from unary

No context.Context parameter and no return-value response — instead you get a stream object and push messages through it:

internal/tom/infra/chat/api.go:19
func (rcv *Server) SubscribeV2(req *pb.SubscribeV2Request,
        srv pb.ChatService_SubscribeV2Server) error {
    userID := interceptors.UserIDFromContext(srv.Context())  // ctx via srv.Context()
    ...
    return clientConn.PumpSubscribeV2()   // loops, calling srv.Send(event) per event
}
Three differences from a unary handler (1) The request is a normal arg, but there's no ctx param — get it from srv.Context(). (2) You don't return a response — you call srv.Send(msg), usually in a loop. (3) You return error (or nil) to end the stream.

When to use it

Server streaming shines when one request yields many or open-ended results: a live feed, server-push notifications, progress updates, or a large result set you'd rather stream than buffer.

Anchor — real-time chat tom is the live chat service; a client calls Subscribe once and tom pushes every new chat event down the stream over a single HTTP/2 connection. Your own three services (conversationmgmt/notification/spike) are unary — tom is the streaming counterpart. That split is itself a design choice: chat delivery streams; chat management is unary CRUD.
Read / do this next

gRPC — Core concepts (Server streaming) + Go Basics tutorial

Core concepts defines the lifecycle; the Go Basics tutorial has you implement a server-streaming method with stream.Send.

grpc.io — server streaming
grpc.io — Go basics

Common mistake Common mistake: assuming streaming is always more advanced and therefore better. If the response is really one finished result, unary is usually simpler.

Check yourself (from memory)

Q1. In server streaming, the stream keyword is on the…

One request → a stream of responses, so stream marks the response: returns (stream Resp).

Q2. A server-streaming Go handler sends messages via…

You push each message with srv.Send(msg) and return nil/err to end. No response is returned.

Q3. Server streaming is a good fit for…

Many/open-ended results from one request — feeds, push, progress. Exactly tom's chat Subscribe.
Describe server streaming and its Go handler shape, with a repo example.
recall, then click to reveal
Client sends ONE request; server returns a STREAM of responses (stream on the response), read until the server closes. Go handler: func(req *Req, srv X_Server) error — no ctx arg (use srv.Context()), no returned response; call srv.Send(msg) in a loop and return nil/err to end. Repo example: tom's Subscribe (chat_modifier.proto:171 → api.go:19), pushing live chat events. Good for feeds, progress, large result sets.
Want to see how the client side reads the stream (for { resp, err := stream.Recv() })? Ask me.

1. gRPC — Core concepts: server streaming.