Skip to main content

Server Streaming RPC

rpc Method(Request) returns (stream Response)

Server streaming sends one request and receives a stream of responses. The server sends multiple messages on the same HTTP/2 stream, keeping it open until the response stream is complete. Ideal for large dataset pagination, real-time feed subscriptions, file downloads, and log tailing where the server generates results over time.

Details

Server streaming allows the server to push multiple responses to a single client request: client sends one request → server streams N responses → server closes stream

HTTP/2 mechanics: the client sends one DATA frame (request). The server sends multiple DATA frames (each response message) and finally a HEADERS frame (trailers with Grpc-Status=0 on success).

Backpressure: HTTP/2 window-based flow control ensures the client can receive at its own pace. If the client's receive window is full, the server's write blocks until the client drains the buffer.

Streaming vs pagination: REST pagination: N round trips for N pages (each page is a new HTTP request) gRPC server streaming: 1 round trip, N messages (one HTTP/2 stream, messages arrive as produced) Server streaming has lower latency for sequential consumption because the server starts sending before the full result is ready.

Error handling: if the server encounters an error mid-stream, it sends trailers with a non-zero Grpc-Status. The client receives all messages sent before the error, then gets the error status.

Common patterns: - List a large dataset: stream records as they are queried from the DB - Subscribe to events: server sends new events as they occur - File download: send chunks as file is read from disk - Search results: stream results as the search engine finds them

Proto definition

Protocol Buffer service definition
syntax = "proto3";

service OrderService {
  // Stream all orders for a customer (large dataset)
  rpc ListOrders(ListOrdersRequest) returns (stream Order);

  // Live order status updates
  rpc WatchOrderStatus(WatchOrderRequest) returns (stream OrderStatus);

  // Stream log entries
  rpc TailLogs(TailLogsRequest) returns (stream LogEntry);
}

message ListOrdersRequest { string customer_id = 1; }
message WatchOrderRequest { string order_id = 1; }

Python server streaming

Python server streaming
# Server (Python)
def ListOrders(self, request, context):
    for order in db.query_orders(request.customer_id):
        yield order   # each yield sends one message to client
        if context.is_active() == False:
            return    # client cancelled, stop sending

# Client (Python)
stream = stub.ListOrders(ListOrdersRequest(customer_id="cust_1"))
for order in stream:     # iterate the stream
    process(order)
# StopIteration raised when server closes stream

When to use

Large dataset streaming

Real-time event feeds

Log tailing

Search result streaming

File downloads in chunks

Backpressure

HTTP/2 flow control – server write blocks when client receive window is full. Client processes at its own rate.

See Also