The Four RPC Types
One keyword changes everything
Section titled “One keyword changes everything”gRPC has exactly four call shapes, and they are all the same idea — a request side and a response side — with the stream keyword added to one side, the other, both, or neither.
service Chat { // 1. Unary: one request → one response rpc GetMessage(GetMessageRequest) returns (Message);
// 2. Server streaming: one request → many responses rpc Subscribe(SubscribeRequest) returns (stream Message);
// 3. Client streaming: many requests → one response rpc Upload(stream Chunk) returns (UploadSummary);
// 4. Bidirectional: many requests ↔ many responses rpc Converse(stream Message) returns (stream Message);}Where stream sits is the whole design decision. Read the signature and you know the call’s shape instantly.
The message flow of each
Section titled “The message flow of each”flowchart TB
subgraph unary["1. Unary"]
direction LR
cu["client"] -->|"1 request"| su["server"]
su -->|"1 response"| cu
end
subgraph ss["2. Server streaming"]
direction LR
cs["client"] -->|"1 request"| sss["server"]
sss -->|"many responses"| cs
end
subgraph cst["3. Client streaming"]
direction LR
ccs["client"] -->|"many requests"| scs["server"]
scs -->|"1 response"| ccs
end
subgraph bidi["4. Bidirectional"]
direction LR
cb["client"] <-->|"many, both ways"| sb["server"]
end 1. Unary — one in, one out
Section titled “1. Unary — one in, one out”The familiar request/response. GetUser(id) returns a User. This is the default and covers the majority of calls: reads, writes, commands. If you’re not sure which type you need, it’s almost always unary.
2. Server streaming — one request, many responses
Section titled “2. Server streaming — one request, many responses”The client asks once; the server sends a sequence of messages back over the open stream, then signals it’s done. Perfect when one request naturally produces many results over time: a subscription to live events, tailing a log, or returning a large result set in chunks so the client can start processing before it’s all computed.
3. Client streaming — many requests, one response
Section titled “3. Client streaming — many requests, one response”The client sends a sequence of messages; the server reads them all and replies once at the end. The classic use is upload: stream a large file in chunks, or send a batch of records, and get a single summary back (bytes received, rows inserted).
4. Bidirectional — many both ways, independently
Section titled “4. Bidirectional — many both ways, independently”Both sides stream over the same connection, and — this is the subtle part — the two directions are independent. The server doesn’t have to wait for the client to finish; messages interleave freely. This is the shape for real-time, conversational workloads: chat, live collaboration, a two-way control channel. It’s the most powerful and the most complex, which is why it gets its own lesson later.
How to choose
Section titled “How to choose”| You need… | Use |
|---|---|
| A normal request/response | Unary |
| To push many updates for one request | Server streaming |
| To send a large upload or batch, get one result | Client streaming |
| A live, two-way conversation | Bidirectional |
A good instinct: start unary. Reach for streaming only when the data genuinely arrives (or leaves) over time and buffering it into one message would be wasteful or too slow. Streaming adds real complexity — ordering, flow control, partial failure — that the Streaming module covers in full.