gRPC is a high-performance, open-source RPC framework that lets a client call methods on a remote server as if they were local. Instead of exposing resources over text-based HTTP/1.x with JSON, gRPC defines strongly typed services and messages, compiles them into code, and transports them over HTTP/2 using binary protobuf payloads. This log focuses on how that model works and when it is a better fit than traditional REST.
How gRPC structures a system
At a high level, a gRPC system consists of:
- Service definitions in
.protofiles. - Generated client and server code from those definitions.
- A gRPC server that implements the service interface.
- Client stubs that expose the same methods as the server.
Server side
- Implements the service interface generated from the
.proto. - Starts a gRPC server to listen for incoming RPC calls.
- Handles serialization and deserialization through generated code.
Client side
- Uses a generated stub that exposes the same methods as the server.
- Calls
stub.method(request)as if it were a local function. - The stub handles marshalling the request into protobuf bytes and sending it over
HTTP/2.
The key idea: you describe services and messages once in .proto, and gRPC generates type-safe, language-specific clients and servers that speak the same wire protocol.
Protocol Buffers: the data model behind gRPC
gRPC uses Protocol Buffers (protobuf) as its default Interface Definition Language and wire format.
You:
- Define messages and services in a
.protofile. - Run the
protoccompiler. - Use generated classes in your application to build, serialize, and parse messages.
Example: .proto for an Employee
// employee.proto
syntax = "proto3";
// Define the Employee message structure
message Employee {
string name = 1;
int32 id = 2;
bool is_full_time = 3;
}
Generated class (conceptual Java example)
// This class is generated from the Employee message definition in employee.proto
public final class Employee extends GeneratedMessageV3 {
private String name;
private int id;
private boolean isFullTime;
public String getName() { return name; }
public int getId() { return id; }
public boolean getIsFullTime() { return isFullTime; }
// Builder-style API and serialization methods are also generated, e.g.:
// Employee.newBuilder().setName("Alice").setId(123).setIsFullTime(true).build();
}
Link between the two:
- The
.protofile describes the schema once. protocgenerates strongly typed classes with:- Field accessors like
getName()andgetId(). - Methods to serialize to bytes and parse from bytes.
- Field accessors like
- Those bytes are what gRPC sends over the network, and both client and server agree on the schema via the shared
.proto.
Example service: combining Employee with a greeting RPC
A simple gRPC service might take an Employee and return a greeting:
// greeter.proto
syntax = "proto3";
import "employee.proto";
service GreeterService {
// RPC method to send greetings with an employee object
rpc SayHelloWithEmployee (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
Employee employee = 1; // Nested Employee message
string message = 2; // Optional message to greet the employee
}
message HelloReply {
string greeting = 1;
}
On the wire, gRPC uses this schema to:
- Serialize
HelloRequestinto protobuf bytes. - Send those bytes over
HTTP/2. - Deserialize them into
HelloRequeston the server. - Return a
HelloReplybuilt in the server implementation.
Request flow: client, stub, server
sequenceDiagram
participant ClientApp
participant Stub as gRPC Client Stub
participant Channel as HTTP/2 Channel
participant Server as gRPC Server
ClientApp->>Stub: SayHelloWithEmployee(employee, message)
Stub->>Channel: Serialize protobuf request over HTTP/2
Channel->>Server: Deliver bytes on RPC stream
Server->>Server: Deserialize to HelloRequest (Employee + message)
Server-->>Channel: Serialize HelloReply
Channel-->>Stub: Return bytes on same stream
Stub-->>ClientApp: HelloReply (greeting string)
Why it matters: the application code never handles raw JSON or HTTP details. It interacts with type-safe methods and message objects; gRPC and protobuf handle serialization, framing, and transport.
gRPC vs REST: protocol and design differences
High-level comparison
| Dimension | gRPC | REST |
|---|---|---|
| Transport | HTTP/2, binary, full-duplex | Typically HTTP/1.1, text-based, request–response |
| Data format | Protocol Buffers (binary, schema-first) | JSON (text, schema-optional) |
| Design style | Function-oriented (rpc methods, request/response messages) | Resource-oriented (GET, POST, PUT, DELETE on resources/URIs) |
| Streaming | Unary, server streaming, client streaming, bidirectional streaming | No native streaming; patterns built on top (SSE, WebSockets, chunked responses) |
| Tooling | .proto, protoc, generated stubs for many languages | OpenAPI/Swagger, manual or generated clients, very wide ecosystem support |
| Use cases | Internal microservices, low-latency systems, real-time workloads | Public APIs, browser clients, cache-friendly, web-facing integrations |
In short: gRPC optimizes for structured, high-performance service-to-service communication, while REST optimizes for simplicity, ubiquity, and web compatibility.
When to choose gRPC
Choose gRPC when:
- You need high throughput and low latency between services.
- Your systems can rely on
HTTP/2(internal networks, service mesh, gRPC-aware infrastructure). - You want strong typing and schema-first contracts:
.protodefinitions reviewed like any other interface.- Generated clients and servers reduce drift and manual boilerplate.
- You need streaming:
- Server-side streaming for push-style updates.
- Client-side streaming for upload pipelines.
- Bidirectional streaming for real-time interactions.
- Your clients are mostly services (Java, Go, Python, Rust, etc.), not browsers.
When to choose REST
Choose REST when:
- You are exposing public APIs to third parties or heterogeneous clients.
- Browser compatibility or simple integration is a priority:
- Browsers speak HTTP and expect JSON over plain HTTP semantics.
- You benefit from cacheability and intermediaries:
GETresponses cacheable via CDNs and proxies.- Simpler debugging with tools like
curl, Postman, browser dev tools.
- Your domain is document- or resource-oriented:
- CRUD-style operations on entities map cleanly to REST resources.
- You need widest ecosystem support and the simplest onboarding for external consumers.
Rule of thumb: gRPC for internal microservice contracts and high-performance backends; REST for public, loosely coupled, or browser-facing APIs.
How both styles coexist in a microservice architecture
In many real systems, both styles coexist:
- Edge/API gateway speaks REST + JSON to browsers or external consumers.
- Internal services speak gRPC + protobuf to each other.
When those internal services persist to globally distributed data stores, the read/write guarantees you rely on become just as important as transport choice—see Consistency Models in Azure Cosmos DB: From Strong to Eventual.
flowchart TD
Browser[Browser or External Client] ---|HTTP 1.1 + JSON| Gateway[API Gateway]
Gateway ---|gRPC + HTTP 2| UserService[User Service]
Gateway ---|gRPC + HTTP 2| OrdersService[Orders Service]
UserService ---|gRPC| InventoryService[Inventory Service]
This hybrid keeps the external surface area simple while reaping the performance and typing benefits of gRPC internally.
Key takeaways
- gRPC is RPC-first and schema-first: you define services and messages in
.protoand generate code; clients call methods as if they were local. - Protocol Buffers provide compact, strongly typed serialization;
.protodefinitions plusprotocproduce message classes and stubs for many languages. - HTTP/2 transport enables multiplexed, low-latency communication and native streaming modes that REST over HTTP/1.1 does not provide.
- REST remains the right choice for public APIs, browser compatibility, caching, and simple integration across many clients.
- In practice, modern architectures often combine both: REST at the edge, gRPC internally, with clear contracts and deliberate tradeoffs for each surface.
// SPONSORSHIP
If this research saved you time or improved your architecture, consider sponsoring my work on GitHub. All sponsorships go directly toward infrastructure and further technical research.
[ Become a Sponsor ]