Web API  

gRPC vs REST

When I first came across gRPC, I found it easy to put it in the same category as REST APIs.

Both allow applications to communicate over a network. Both have clients and servers. Both can send requests and receive responses.

So why would we need another way to build APIs?

The difference becomes much clearer when you stop thinking about gRPC as simply "another REST alternative" and instead think about it as a way for one application to call a method on another application.

That is the idea behind Remote Procedure Call (RPC).

In this article, we'll start from the basics, look at where gRPC makes sense, and compare it with REST.

What problem does gRPC solve?

Imagine you have an e-commerce system composed of several services:

1111

The Order Service needs information from the Customer Service.

With a traditional REST API, you might expose:

GET /api/customers/111

You're basically saying:

"Give me the customer with ID 111."

and receive:

{
  "id": 111,
  "name": "John",
  "email": "[email protected]"
}

The client sends an HTTP request, the server processes it, then the server sends back a response.

gRPC gives you another way of thinking about this communication.

Instead of thinking primarily in terms of URLs and HTTP endpoints, you think in terms of calling a method on another service.

It's as if you're saying:

"Call the GetCustomer method with 111."

That is where the name comes from: Remote Procedure Call (RPC).

                    ─── GET /customers/111 ───>
HTTP API       Client                         Server
                    <── JSON Customer ─────────
                    ─── GetCustomer(111) ─────>
RPC / gRPC      Client                         Server
                    <── Customer ─────────────

The important thing to understand is that RPC doesn't replace HTTP.

gRPC, for example, commonly uses HTTP/2 underneath:

Application A  ->  gRPC  ->  HTTP/2  ->  Network  ->  HTTP/2  ->  gRPC  ->  Application B

So let's first understand what RPC actually means.

So, what is RPC?

A Remote Procedure Call is a way for one application to call a function or method that is running in another application or on another machine.

For example, locally you might write:

var order = GetOrder(111);

GetOrder() runs inside your application.

With RPC, the idea is similar:

var order = orderClient.GetOrder(111);

But GetOrder() might actually execute inside a completely different service:

11111

The important difference is that the method is remote. Your application calls it as if it were a normal method, but the actual work happens somewhere else.

Then, what is gRPC?

gRPC is a framework for implementing this RPC approach. It was originally created by Google and is now an open-source project.

With gRPC, you define the methods that a service exposes in a contract.

For example:

service OrderService {
    rpc GetOrder(GetOrderRequest) returns (Order);
}

This essentially says:

"There is an OrderService with a GetOrder method. It receives a GetOrderRequest and returns an Order."

From this contract, tooling can generate client and server code for you.

That's one of the things that makes gRPC attractive for communication between services. Instead of manually defining and maintaining the communication structure in each application, both sides can work from the same contract.

But this raises another question.

How do the client and server represent the Order and GetOrderRequest objects that they're sending to each other?

That's where Protocol Buffers come in.

But what is Protobuf?

This is the next concept that usually causes confusion.

When two applications communicate, they need a way to represent the data being sent.

For example, suppose we want to send:

new Order
{
    Id = 123,
    CustomerName = "Mariem",
    Total = 150
};

The receiving application can't directly receive your C# object.

The object needs to be serialized into a format that can travel over the network.

You may already be familiar with JSON:

{
  "id": 123,
  "customerName": "Mariem",
  "total": 150
}

JSON is one way of representing the data.

Protocol Buffers (Protobuf) is another.

Instead of describing the data as JSON, you define its structure in a .proto file:

message Order {
    int32 id = 1;
    string customer_name = 2;
    double total = 3;
}

The .proto file describes the structure of the messages exchanged between the client and server.

Protobuf then provides a compact binary serialization format for those messages.

So, very roughly:

C# object  ->  Protobuf serialization  ->  Binary data  ->  Network  ->  Protobuf deserialization  ->  C# object

Why Protocol Buffers?

You might be wondering:

"If I already have JSON, why introduce Protobuf?"

Because Protobuf is designed to be compact and efficient.

Unlike JSON, the serialized Protobuf message isn't designed to be read by humans. That's intentional.

The advantage is that messages can be considerably smaller and can be efficiently serialized and deserialized.

This can be particularly useful when services communicate frequently or when network bandwidth and latency matter.

And where does HTTP/2 fit?

At this point, we have:

RPC  ->  gRPC  ->  Protobuf

But we still need a way to transport the messages between the client and server.

gRPC commonly uses HTTP/2 as its transport.

HTTP/2 brings capabilities that are particularly useful for service-to-service communication, such as

  • sending multiple requests over a single connection

  • support for bidirectional streaming.

So you can think about the pieces like this:

111111

We can summarize it as:

gRPC is a high-performance framework for allowing applications and services to communicate by calling remote methods. It commonly uses HTTP/2 to transport the communication and Protocol Buffers (Protobuf) to define and serialize the messages exchanged between them.

gRPC becomes particularly interesting when you have multiple services communicating frequently with each other and you start caring about things such as serialization efficiency, strongly typed contracts, latency, and streaming.

This is one reason gRPC is commonly used for communication between internal services in distributed systems and microservice architectures.

The Four Types of gRPC Calls

One of my favorite things about gRPC is that a method doesn't have to be limited to:

Request  ->  Response

gRPC supports four different communication patterns.

1. Unary RPC

This is the closest equivalent to a normal REST request.

      -------> Request ------->
Client                       Server
       <------ Response <------

Example:

rpc GetCustomer(GetCustomerRequest)
    returns (CustomerResponse);

The client sends one request and receives one response.

This is probably the first type you'll use when learning gRPC, and conceptually it's the simplest.

2. Server Streaming

The client sends one request, but the server can send multiple responses.

      -------> Request ------->
Client                       Server
       <------ Response <-------
       <------ Response <-------
       <------ Response <-------

For example, imagine a service monitoring a machine.

The client might say:

"Start monitoring machine 42."

The server could then continuously send:

CPU: 42%
CPU: 45%
CPU: 51%
CPU: 48%
...

This is much more natural than repeatedly polling an HTTP endpoint to ask:

"Do you have new data?"

3. Client Streaming

Now the client sends multiple messages to the server.

      
       -------> Request ------->
       -------> Request ------->
       -------> Request ------->
Client                       Server
       <------ Response <-------

For example, imagine uploading a large amount of data in chunks.

Instead of sending one enormous request, the client can stream chunks to the server.

Once the server has received the stream, it can send a response.

4. Bidirectional Streaming

This is where things get really interesting.

Both sides can continuously send messages.

Client                       Server
   |    -------> Request ------->              |
   |    <------ Response <------               |
   |    -------> Request ------->              |
   |    <------ Response <------               |
   |    -------> Request  ------>               |
   |    <------ Response <------              |

The client and server don't have to wait for the other side to finish the entire stream.

This is useful for things such as real-time communication, telemetry, monitoring, collaborative applications, and other continuous data flows.

When Should You Use gRPC?

This is probably the more important question.

I wouldn't recommend replacing every REST API in your system with gRPC.

gRPC is particularly attractive when applications need to communicate efficiently with other applications, especially when you control both sides of the communication.

This is a common microservices scenario:

Order Service  ->  gRPC  ->  Customer Service

Other good scenarios include high-frequency service-to-service communication, systems where strongly typed contracts are important, polyglot environments where different services use different programming languages, and applications that benefit from streaming.

For example, if you have several backend services communicating thousands of times per second, reducing serialization overhead and network traffic can become important.

When Would I Prefer REST?

REST/HTTP APIs are often a better choice when your API is primarily consumed by browsers, external developers, or third-party systems.

JSON is easy to read, easy to debug, and supported almost everywhere. A developer can make a request using a browser, Postman, curl, or practically any programming language without needing specialized generated client code.

Browser support is another important consideration. Standard HTTP APIs are a natural fit for browser-based applications.

For example:

Browser  ->  HTTP/JSON API  ->  Backend

On the other hand, for communication between backend services that you control:

Service A  ->  gRPC  ->  Service B

gRPC can be a very good fit.

The most useful way to think about it isn't:

"Is gRPC better than REST?"

Instead, ask:

"What kind of communication does my application need?"

If you need a simple API that browsers, external clients, and third parties can easily consume, REST is often the simpler choice.

If you're building backend-to-backend communication and care about strongly typed contracts, efficient serialization, high-frequency communication, or streaming, gRPC becomes much more interesting.