What Is a Unary RPC?
A unary RPC is the simplest gRPC call pattern: the client sends a single request message and the server returns a single response message, much like a traditional function call made over the network. Under HTTP/2 this maps to one stream carrying exactly one request and one response, with the stream closing once the response and trailing metadata arrive. Unary RPCs are the default choice for typical request/response interactions such as fetching a user profile, submitting a form, or triggering a one-off computation.
Cricket analogy: It is like a single review referral in cricket: the on-field umpire sends one query to the third umpire, and exactly one verdict — out or not out — comes back before play resumes.
How Unary RPCs Work Under the Hood
When a client invokes a unary method, the gRPC runtime opens a new HTTP/2 stream, sends a HEADERS frame containing the method name and metadata, then a DATA frame containing the serialized protobuf request. The server processes the request and replies with its own HEADERS frame, a DATA frame holding the serialized response, and a trailing HEADERS frame carrying the grpc-status and grpc-message. Because everything rides on a single logical stream within one shared HTTP/2 connection, many concurrent unary calls can be multiplexed without the head-of-line blocking that plagued HTTP/1.1.
Cricket analogy: It is like the sequence of a DRS review — captain signals for review, ball-tracking data is sent, Hawk-Eye renders a verdict, and the decision is announced — a fixed handshake of steps every single time.
Defining a Unary RPC in Protocol Buffers
syntax = "proto3";
package userservice.v1;
service UserService {
// Unary RPC: one GetUserRequest in, one GetUserResponse out.
rpc GetUser(GetUserRequest) returns (GetUserResponse);
}
message GetUserRequest {
string user_id = 1;
}
message GetUserResponse {
string user_id = 1;
string display_name = 2;
string email = 3;
}import grpc
from userservice.v1 import userservice_pb2, userservice_pb2_grpc
channel = grpc.insecure_channel("localhost:50051")
stub = userservice_pb2_grpc.UserServiceStub(channel)
try:
response = stub.GetUser(
userservice_pb2.GetUserRequest(user_id="u-42"),
timeout=2.0, # deadline in seconds
)
print(response.display_name)
except grpc.RpcError as e:
print(f"RPC failed: {e.code()} - {e.details()}")Because a unary call is a single bounded exchange, gRPC clients almost always attach a deadline to it, propagated as the grpc-timeout header. If the deadline lapses before the server responds, the client library raises a DEADLINE_EXCEEDED status locally and the runtime sends a cancellation to the server so it can stop wasted work. Servers should treat every unary handler as potentially cancellable and check the context for cancellation on long-running internal operations, rather than assuming the call will always run to completion.
Cricket analogy: It is like the third umpire having a fixed 60-second window to confirm a stumping — if no decision arrives in time, the on-field call simply stands, exactly as a deadline forces a fallback.
When to Use Unary RPCs
Unary RPCs are the right default for CRUD-style operations, authentication checks, and any interaction that naturally has one logical answer per request, because they are the easiest pattern to reason about, retry safely, and load-balance across replicas. Reach for streaming RPCs instead when a single call would otherwise need to return an unbounded or very large sequence of items, when the client needs to push a continuous flow of data such as telemetry, or when both sides need to exchange messages in real time without re-establishing a connection for every message.
Cricket analogy: Choosing unary over streaming is like calling for a single ball-by-ball scorecard update rather than subscribing to a live commentary feed — fine when you only need one data point, not the whole innings.
Always set an explicit deadline on unary calls (e.g. context.WithTimeout in Go, timeout= in Python). Without one, a slow or hung server can leave client resources tied up indefinitely, since gRPC does not impose a default timeout.
- A unary RPC sends exactly one request and receives exactly one response over a single HTTP/2 stream.
- The stream carries HEADERS, DATA, and trailing HEADERS frames for both the request and the response.
- Unary calls should always carry an explicit client-side deadline to avoid resources hanging indefinitely.
- Servers should check for context cancellation during long-running unary handlers rather than assuming completion.
- gRPC status codes such as DEADLINE_EXCEEDED and CANCELLED are delivered via the trailing metadata.
- Unary RPCs are the right default for CRUD-style, one-answer-per-request operations.
- Choose a streaming RPC instead when data is unbounded, continuous, or needs bidirectional real-time exchange.
Practice what you learned
1. What best characterizes a unary RPC in gRPC?
2. What happens if a client-side deadline is exceeded on a unary call?
3. Which HTTP/2 mechanism allows many unary calls to run concurrently over one TCP connection?
4. When is a unary RPC the wrong choice?
Was this page helpful?
You May Also Like
Server Streaming RPC
A gRPC pattern where the client sends one request and the server replies with a sequence of messages over time on the same stream.
Client Streaming RPC
A gRPC pattern where the client sends a sequence of messages over one stream and the server replies with a single response once the client finishes.
Choosing the Right RPC Type
A practical decision guide for picking between unary, server-streaming, client-streaming, and bidirectional-streaming gRPC methods.
Related Reading
Related Study Notes in Web Development
Browse all study notesWebSockets Study Notes
Web Development · 30 topics
Web DevelopmentWebAssembly Study Notes
WebAssembly · 30 topics
Web DevelopmentSpring Boot Study Notes
Java · 30 topics
Web DevelopmentFlask Study Notes
Python · 30 topics
Web DevelopmentDjango Study Notes
Python · 30 topics
Web DevelopmentNext.js Study Notes
JavaScript · 30 topics