Skip to content

The Four RPC Types

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.

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
The four RPC types, by where messages flow

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.

You need…Use
A normal request/responseUnary
To push many updates for one requestServer streaming
To send a large upload or batch, get one resultClient streaming
A live, two-way conversationBidirectional

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.

What single keyword determines a method's streaming shape?
Which RPC type sends one request and receives many responses?
A client uploads a large file in chunks and wants a single summary back. Which type fits?
What makes bidirectional streaming distinct from just doing both other streams?