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.

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

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.