Logo
Overview
gRPC: Why It Exists and How It Actually Works

gRPC: Why It Exists and How It Actually Works

August 11, 2026
11 min read

gRPC is what you reach for when REST starts feeling like the wrong tool: when services talk to each other a lot, latency matters, you control both ends, and you want strong contracts and streaming instead of “just JSON over HTTP.”

Here’s a full walk‑through from basics to the more complex pieces in your notes.


1. Why RPC exists at all

Remote Procedure Call in one sentence

RPC (Remote Procedure Call) lets one process call a function that actually runs on another machine, as if it were a local function call.

Instead of:

  • manually opening sockets,
  • crafting HTTP requests,
  • serializing data,
  • parsing responses,

you call something like:

processPayment(userId, amount)

and the RPC framework turns that into:

  1. Serialize arguments.
  2. Send over the network.
  3. Execute processPayment on the server.
  4. Send back the result.
  5. Deserialize on the client.

This abstraction is old (pre‑web), but it maps perfectly to microservices: services are just processes calling each other’s methods remotely.


2. Why gRPC was needed

REST started to creak for service‑to‑service traffic

REST/JSON over HTTP became the default for web APIs because it’s:

  • simple,
  • human‑readable,
  • great for browsers and external clients.

But inside a microservice architecture, REST has drawbacks:

  • Verbose payloads – JSON is text. For high‑throughput internal calls, verbose messages hurt latency and bandwidth.
  • Weak typing – JSON is flexible but loosely typed; you rely on conventions, not a single enforced contract.
  • Streaming is awkward – you end up using long‑polling, WebSockets, or custom hacks for bidirectional streams.
  • Code generation is optional – many teams hand‑write clients, increasing friction and bugs.

Google and others needed something:

  • fast,
  • strongly typed,
  • streaming‑friendly,
  • and language‑independent,

for backend‑to‑backend communication. That’s where gRPC comes in.


3. What gRPC is (and isn’t)

Definition

gRPC (Google Remote Procedure Call) is an open‑source RPC framework developed by Google:

  • It runs over HTTP/2.
  • It uses Protocol Buffers (Protobuf) by default for serialization.
  • It gives you contract‑first APIs via .proto files.
  • It supports four communication patterns: unary, server streaming, client streaming, bidirectional streaming.

You reach for gRPC when:

  • you control both client and server,
  • you want strong, code‑generated contracts,
  • you need streaming and low latency,
  • it’s mostly microservice traffic, not public browser traffic.

It’s not intended as a drop‑in replacement for HTML/JSON web APIs to random browsers.


4. Protocol Buffers: the contract and the bytes

Protobuf as schema

Protocol Buffers are both:

  • an interface definition language (IDL) and
  • a binary serialization format.

You define your API in a .proto file:

syntax = "proto3";
service PaymentService {
rpc ProcessPayment(PaymentRequest)
returns (PaymentResponse);
}
message PaymentRequest {
int32 user_id = 1;
double amount = 2;
}
message PaymentResponse {
bool success = 1;
string message = 2;
}

This .proto file is:

  • the contract between client and server,
  • the source of truth for:
    • method names,
    • request/response types,
    • field types and tags.

Why Protobuf over JSON

Protobuf gives you:

  • Strong typing – fields have explicit types and ids; incompatible changes show up early.
  • Language independence – the same .proto file can generate Java, Go, Python, C#, Rust, etc. code.
  • Compact binary encoding – messages are densely encoded:
    • usually smaller than equivalent JSON,
    • faster to serialize/deserialize.

Protobuf isn’t “5× faster than JSON” in some absolute sense, but multiple benchmarks show significant gains in throughput and latency for typical microservice workloads.


5. Code generation: stubs on both sides

Once you have a .proto file, you run the Protobuf compiler (protoc) with language‑specific plugins:

payment.proto
|
v
protoc + plugins
|
+---------+---------+
| |
v v
Client stub Server skeleton
(e.g., Java) (e.g., Go)

On the client:

  • The client stub exposes a strongly typed method:
    • ProcessPayment(PaymentRequest) -> PaymentResponse.
  • Your application calls that method like a local function.
  • The stub:
    • serializes the request,
    • sends it over HTTP/2,
    • deserializes the response.

On the server:

  • The server stub deserializes the incoming request.
  • Calls your implementation of ProcessPayment.
  • Serializes and sends the response back.

You write business logic; gRPC + generated stubs handle all the transport, framing, and serialization.


6. HTTP/2: the performance foundation

gRPC’s performance isn’t magic; it comes from building on HTTP/2, which adds features above plain HTTP/1.1:

6.1 Multiplexing

In HTTP/1.1, you often end up with:

  • many separate TCP connections,
  • or head‑of‑line blocking on a single connection.

HTTP/2 introduces multiplexed streams:

  • One TCP connection.
  • Many logical streams (each an RPC call).
  • All streams can be active concurrently.

This reduces connection overhead and improves utilization for chatty microservices.

6.2 Binary framing

HTTP/2 breaks messages into binary frames:

  • HEADERS frames carry metadata.
  • DATA frames carry payloads.
  • Frames from different streams can interleave on the wire.

gRPC rides on top of this, mapping Protobuf messages to HTTP/2 frames. This is ideal for:

  • streaming responses,
  • partial results,
  • long‑lived streams with many messages.

6.3 Full duplex

HTTP/2 supports full‑duplex: client and server can both send data at the same time over the same connection.

That’s what enables:

  • server streaming,
  • client streaming,
  • bidirectional streaming,

over one persistent connection, which is hard to do cleanly with plain REST without websockets or custom protocols.


7. Four gRPC communication patterns

This is one of the most important parts of gRPC.

Four gRPC communication patterns

7.1 Unary RPC (simple request–response)

Signature:

rpc GetUser(GetUserRequest) returns (User);

Behavior:

  • Client sends one request.
  • Server sends one response.

This is the gRPC equivalent of a traditional REST call. Most CRUD operations and simple queries fit here.


7.2 Server streaming RPC

Signature:

rpc ListUpdates(UpdatesRequest) returns (stream Update);

Behavior:

  • Client sends one request.
  • Server sends a stream of responses:
    • e.g. Update 1, Update 2, Update 3, …

Use cases:

  • live notifications,
  • stock price feeds,
  • log tailing,
  • large result sets sent gradually.

The client reads messages until the server closes the stream.


7.3 Client streaming RPC

Signature:

rpc UploadChunks(stream Chunk) returns (UploadResult);

Behavior:

  • Client sends a stream of requests.
  • Server sends one response when done.

Use cases:

  • file uploads in chunks,
  • batched telemetry,
  • submitting many data points for aggregation.

The client writes messages until it’s done, then half‑closes its side; the server processes the stream and replies once.


7.4 Bidirectional streaming RPC (bidi)

Signature:

rpc Chat(stream ChatMessage) returns (stream ChatMessage);

Behavior:

  • Both client and server send independent streams.
  • Either side can send messages at any time.
  • All messages share one underlying HTTP/2 stream.

Use cases:

  • chat and messaging,
  • multiplayer gaming,
  • collaborative editing,
  • real‑time control systems.

This is gRPC’s most powerful pattern; it’s “true full duplex” over a standard HTTP/2 connection.


7.5 Quick summary

PatternClient messagesServer messagesTypical use
Unary11CRUD, simple queries
Server streaming1manyfeeds, large results, subscriptions
Client streamingmany1uploads, batching, aggregation
Bidirectionalmanymanychat, gaming, realtime sync

Most systems start with unary and add streaming where it naturally fits.


8. gRPC architecture: end-to-end call flow

gRPC end-to-end call flow

Putting it together, a gRPC call flows like this:

  1. Client app calls a method on the generated client stub:
    • paymentClient.ProcessPayment(request).
  2. Client stub:
    • validates types,
    • serializes PaymentRequest via Protobuf,
    • hands data to the gRPC runtime.
  3. Client runtime:
    • creates or reuses an HTTP/2 stream,
    • sends HEADERS + DATA frames over the network.
  4. Server runtime:
    • receives frames,
    • reassembles the request,
    • deserializes into a PaymentRequest object.
  5. Server stub:
    • invokes your ProcessPayment implementation.
  6. Server app processes and returns a PaymentResponse.
  7. Server stub + runtime serialize and send the response frames.
  8. Client runtime + stub:
    • receive frames,
    • deserialize into PaymentResponse,
    • return it to your app code.

You never touch raw frames or sockets; you work in terms of typed method calls.


In a microservice architecture, you often have:

  • dozens or hundreds of services,
  • written in multiple languages,
  • talking to each other constantly.

gRPC is attractive here because:

  • Contract-first:
    • .proto file is the canonical API spec.
    • Every team sees the same schema.
  • Language independence:
    • Java order service,
    • Go payment service,
    • Python inventory service,
    • all sharing one .proto contract.
  • Performance:
    • small binary messages,
    • multiplexing over HTTP/2,
    • good fit for high‑throughput internal traffic.
  • Streaming support:
    • natural fit for event streams, long‑running tasks, real‑time signals.
  • Tooling:
    • first‑class support in most modern languages,
    • ecosystem of interceptors, observability integrations, gateways.

Most “real” usage today is service‑to‑service in data centers, often behind an API gateway that exposes REST/JSON to external clients and gRPC internally.


10. Why gRPC is not everywhere in browsers

Your notes ask the right question:

If gRPC is so good, why isn’t it used directly between web clients and web servers?

Main reasons:

  • Browsers don’t expose full HTTP/2 control to JavaScript:
    • You work through fetch, XHR, WebSockets, etc.
    • You can’t just open arbitrary HTTP/2 streams and send Protobuf frames.
  • Native gRPC requires:
    • specific headers,
    • trailers,
    • framing semantics,
    • that browsers don’t give you out of the box.

So you can’t implement a full native gRPC client in the browser using only standard web APIs.

10.1 gRPC-Web

gRPC-Web is the workaround:

  • Browser talks gRPC-Web (a simplified, HTTP/1.1/2‑friendly variant).
  • A gRPC-Web proxy (Envoy, API gateway) translates gRPC-Web requests into native gRPC over HTTP/2 for the backend server.

Architecture:

Browser JS
|
| gRPC-Web (HTTP/1.1/2)
v
Proxy / Gateway
|
| gRPC (HTTP/2 + Protobuf)
v
Backend gRPC service

gRPC-Web makes it possible to use gRPC‑style APIs from browsers, but it:

  • supports fewer features than native gRPC (limited streaming, metadata handling),
  • adds another moving part (proxy/gateway).

That’s why REST/JSON is still the default for public web APIs, while gRPC dominates internal microservices.


11. Advantages and limitations in practice

11.1 Advantages

Summarizing the major advantages:

  1. High performance
    • Efficient binary Protobuf messages.
    • HTTP/2 multiplexing and streaming.
  2. Strongly typed contracts
    • .proto definitions enforce types and shapes.
    • Easier to evolve than ad‑hoc JSON.
  3. Automatic code generation
    • Client and server stubs in multiple languages.
    • Less manual plumbing code.
  4. Language independence
    • One contract, many implementations.
  5. Streaming support
    • Server, client, and bidirectional streaming out of the box.
  6. Deadline and cancellation propagation
    • You can set deadlines/timeouts and propagate them through calls.
  7. Integration with TLS and auth
    • Works with standard mTLS, token‑based auth, interceptors.
  8. Ideal for internal microservice traffic
    • Where you control both ends and can invest in tooling.

11.2 Limitations

Important caveats:

  1. Browser support is indirect
    • Native gRPC isn’t usable from JavaScript alone; you need gRPC-Web + proxy.
  2. Less human-readable
    • Protobuf messages are binary; debugging on the wire requires tools.
  3. Public API ergonomics
    • External developers often prefer REST/JSON with simple tools.
  4. Contract discipline
    • You must manage .proto files carefully:
      • versioning,
      • backward compatibility,
      • deprecation policies.
  5. Infrastructure requirements
    • Load balancers, API gateways, and observability tools must handle HTTP/2 and streams correctly (especially bidirectional streaming).

In other words: gRPC is great when you own the stack, but not always the simplest choice for “any client on the internet.”


12. One mental model to remember

The simplest way to hold gRPC in your head:

“It’s function calls over HTTP/2, with a .proto file as the source of truth.”

Workflow:

  1. You define a service and messages in .proto.
  2. You generate client and server stubs.
  3. Your code calls methods on the client stub.
  4. gRPC + Protobuf handle the network, framing, serialization.
  5. The server implements those methods.

You get:

  • strong contracts,
  • streaming,
  • performance,

without writing network plumbing by hand.

That’s why gRPC became the “inter‑service communication mechanism of choice” inside many modern microservice architectures.


13. References