Cover image
Back-end
9 minute read

gRPC vs. REST: Getting Started With the Best API Protocol

gRPC, REST’s up-and-coming competitor, approaches synchronous communication from another angle, offering protocol buffers and typed contracts. What does that mean for your project?

In today’s technology landscape, most projects require the use of APIs. APIs bridge communication between services that may represent a single, complex system but may also reside on separate machines or use multiple, incompatible networks or languages.

Many standard technologies address the interservice communication needs of distributed systems, such as REST, SOAP, GraphQL, or gRPC. While REST is a favored approach, gRPC is a worthy contender, offering high performance, typed contracts, and excellent tooling.

REST Overview

Representational state transfer (REST) is a means of retrieving or manipulating a service’s data. A REST API is generally built on the HTTP protocol, using a URI to select a resource and an HTTP verb (e.g., GET, PUT, POST) to select the desired operation. Request and response bodies contain data that is specific to the operation, while their headers provide metadata. To illustrate, let’s look at a simplified example of retrieving a product via a REST API.

Here, we request a product resource with an ID of 11 and direct the API to respond in JSON format:

GET /products/11 HTTP/1.1
Accept: application/json

Given this request, our response (irrelevant headers omitted) may look like:

HTTP/1.1 200 OK
Content-Type: application/json

{ id: 11, name: "Purple Bowtie", sku: "purbow", price: { amount: 100, currencyCode: "USD"  }  }

While JSON may be human-readable, it is not optimal when used between services. The repetitive nature of referencing property names—even when compressed—can lead to bloated messages. Let’s look at an alternative to address this concern.

gRPC Overview

gRPC Remote Procedure Call (gRPC) is an open-source, contract-based, cross-platform communication protocol that simplifies and manages interservice communication by exposing a set of functions to external clients.

Built on top of HTTP/2, gRPC leverages features such as bidirectional streaming and built-in Transport Layer Security (TLS). gRPC enables more efficient communication through serialized binary payloads. It uses protocol buffers by default as its mechanism for structured data serialization, similar to REST’s use of JSON.

Unlike JSON, however, protocol buffers are more than a serialized format. They include three other major parts:

  • A contract definition language found in .proto files (We’ll follow proto3, the latest protocol buffer language specification.)
  • Generated accessor-function code
  • Language-specific runtime libraries

The remote functions that are available on a service (defined in a .proto file) are listed inside the service node in the protocol buffer file. As developers, we get to define these functions and their parameters using protocol buffers’ rich type system. This system supports various numeric and date types, lists, dictionaries, and nullables to define our input and output messages.

These service definitions need to be available to both the server and the client. Unfortunately, there is no default mechanism to share these definitions aside from providing direct access to the .proto file itself.

This example .proto file defines a function to return a product entry, given an ID:

syntax = "proto3";

package product;

service ProductCatalog {
    rpc GetProductDetails (ProductDetailsRequest) returns (ProductDetailsReply);
}

message ProductDetailsRequest {
    int32 id = 1;
}

message ProductDetailsReply {
    int32 id = 1;
    string name = 2;
    string sku = 3;
    Price price = 4;
}

message Price {
    float amount = 1;
    string currencyCode = 2;
}
Snippet 1: ProductCatalog Service Definition

The strict typing and field ordering of proto3 make message deserialization considerably less taxing than parsing JSON.

Comparing REST vs. gRPC

To recap, the most significant points when comparing REST vs. gRPC are:

  REST gRPC
Cross-platform Yes Yes
Message Format Custom but generally JSON or XML Protocol buffers
Message Payload Size Medium/Large Small
Processing Complexity Higher (text parsing) Lower (well-defined binary structure)
Browser Support Yes (native) Yes (via gRPC-Web)

Where less-strict contracts and frequent additions to the payload are expected, JSON and REST are great fits. When contracts tend to stay more static and speed is of the utmost importance, gRPC generally wins out. In most projects I have worked on, gRPC has proved to be lighter and more performant than REST.

gRPC Service Implementation

Let’s build a streamlined project to explore how simple it is to adopt gRPC.

Creating the API Project

To get started, we will create a .NET 6 project in Visual Studio 2022 Community Edition (VS). We will select the ASP.NET Core gRPC Service template and name both the project (we’ll use InventoryAPI) and our first solution within it (Inventory).

A "Configure your new project" dialog within Visual Studio 2022. In this screen we typed "InventoryAPI" in the Project name field, we selected "C:\MyInventoryService" in the Location field, and typed "Inventory" in the Solution name field. We left "Place solution and project in the same directory" unchecked.

Now, let’s choose the .NET 6.0 (Long-term support) option for our framework:

An Additional information dialog within Visual Studio 2022. In this screen we selected ".NET 6.0 (Long-term support)" from the Framework dropdown. We left "Enable Docker" unchecked.

Defining Our Product Service

Now that we’ve created the project, VS displays a sample gRPC prototype definition service named Greeter. We will repurpose Greeter’s core files to suit our needs.

  • To create our contract, we will replace the contents of greet.proto with Snippet 1, renaming the file product.proto.
  • To create our service, we will replace the contents of the GreeterService.cs file with Snippet 2, renaming the file ProductCatalogService.cs.
using Grpc.Core;
using Product;

namespace InventoryAPI.Services
{
    public class ProductCatalogService : ProductCatalog.ProductCatalogBase
    {
        public override Task<ProductDetailsReply> GetProductDetails(
            ProductDetailsRequest request, ServerCallContext context)
        {
            return Task.FromResult(new ProductDetailsReply
            {
                Id = request.Id,
                Name = "Purple Bowtie",
                Sku = "purbow",
                Price = new Price
                {
                    Amount = 100,
                    CurrencyCode = "USD"
                }
            });
        }
    }
}
Snippet 2: ProductCatalogService

The service now returns a hardcoded product. To make the service work, we need only change the service registration in Program.cs to reference the new service name. In our case, we will rename app.MapGrpcService<GreeterService>(); to app.MapGrpcService<ProductCatalogService>(); to make our new API runnable.

Fair Warning: Not Your Standard Protocol Test

While we may be tempted to try it, we cannot test our gRPC service through a browser aimed at its endpoint. If we were to attempt this, we would receive an error message indicating that communication with gRPC endpoints must be made through a gRPC client.

Creating the Client

To test our service, let’s use VS’s basic Console App template and create a gRPC client to call the API. I named mine InventoryApp.

For expediency, let’s reference a relative file path by which we will share our contract. We will add the reference manually to the .csproj file. Then, we’ll update the path and set Client mode. Note: I recommend you become familiar with and have confidence in your local folder structure before using relative referencing.

Here are the .proto references, as they appear in both the service and client project files:

Service Project File
(Code to copy to client project file)
Client Project File
(After pasting and editing)
  <ItemGroup>
    <Content Update="Protos\product.proto" GrpcServices="Server" />
  </ItemGroup>
  <ItemGroup>
    <Protobuf Include="..\InventoryAPI\Protos\product.proto" GrpcServices="Client" />
  </ItemGroup>

Now, to call our service, we’ll replace the contents of Program.cs. Our code will accomplish a number of objectives:

  1. Create a channel that represents the location of the service endpoint (the port may vary, so consult the launchsettings.json file for the actual value).
  2. Create the client object.
  3. Construct a simple request.
  4. Send the request.
using System.Text.Json;
using Grpc.Net.Client;
using Product;

var channel = GrpcChannel.ForAddress("https://localhost:7200");
var client = new ProductCatalog.ProductCatalogClient(channel);

var request = new ProductDetailsRequest
{
    Id = 1
};

var response = await client.GetProductDetailsAsync(request);

Console.WriteLine(JsonSerializer.Serialize(response, new JsonSerializerOptions
{
    WriteIndented = true
}));
Console.ReadKey();
Snippet 3: New Program.cs

Preparing for Launch

To test our code, in VS, we’ll right-click the solution and choose Set Startup Projects. In the Solution Property Pages dialog, we’ll:

  • Select the radio button beside Multiple startup projects, and in the Action drop-down menu, set both projects (InventoryAPI and InventoryApp) to Start.
  • Click OK.

Now we can start the solution by clicking Start in the VS toolbar (or by pressing the F5 key). Two new console windows will display: one to tell us the service is listening, the other to show us details of the retrieved product.

gRPC Contract Sharing

Now let’s use another method to connect the gRPC client to our service’s definition. The most client-accessible contract-sharing solution is to make our definitions available through a URL. Other options are either very brittle (file shared through a path) or require more effort (contract shared through a native package). Sharing through a URL (as SOAP and Swagger/OpenAPI do) is flexible and requires less code.

To get started, make the .proto file available as static content. We will update our code manually because the UI on the build action is set to “Protobuf Compiler.” This change directs the compiler to copy the .proto file so it may be served from a web address. If this setting were changed through the VS UI, the build would break. Our first step, then, is to add Snippet 4 to the InventoryAPI.csproj file:

  <ItemGroup>
    <Content Update="Protos\product.proto">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Content>
  </ItemGroup>

  <ItemGroup>
    <Content Include="Protos\product.proto" CopyToPublishDirectory="PreserveNewest" />
  </ItemGroup>
Snippet 4: Code to Add to the InventoryAPI Service Project File

Next, we insert the code in Snippet 5 at the top of the ProductCatalogService.cs file to set up an endpoint to return our .proto file:

using System.Net.Mime;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.FileProviders;
Snippet 5: Namespace Imports

And now, we add Snippet 6 just before app.Run(), also in the ProductCatalogService.cs file:

var provider = new FileExtensionContentTypeProvider();
provider.Mappings.Clear();
provider.Mappings[".proto"] = MediaTypeNames.Text.Plain;
app.UseStaticFiles(new StaticFileOptions
{
    FileProvider = new PhysicalFileProvider(Path.Combine(app.Environment.ContentRootPath, "Protos")),
    RequestPath = "/proto",
    ContentTypeProvider = provider
});

app.UseRouting();
Snippet 6: Code to Make .proto Files Accessible Through the API

With Snippets 4-6 added, the contents of the .proto file should be visible in the browser.

A New Test Client

Now we want to create a new console client that we will connect to our existing server with VS’s Dependency Wizard. The issue is that this wizard doesn’t talk HTTP/2. Therefore, we need to adjust our server to talk over HTTP/1 and start the server. With our server now making its .proto file available, we can build a new test client that hooks into our server via the gRPC wizard.

  1. To change our server to talk over HTTP/1, we’ll edit our appsettings.json JSON file:
    1. Adjust the Protocol field (found at the path Kestrel.EndpointDefaults.Protocols) to read Https.
    2. Save the file.
  2. For our new client to read this proto information, the server must be running. Originally, we started both the previous client and our server from VS’s Set Startup Projects dialog. Adjust the server solution to start only the server project, then start the solution. (Now that we have modified the HTTP version, our old client can no longer communicate with the server.)
  3. Next, create the new test client. Launch another instance of VS. We’ll repeat the steps as detailed in the Creating the API Project section, but this time, we’ll choose the Console App template. We’ll name our project and solution InventoryAppConnected.
  4. With the client chassis created, we’ll connect to our gRPC server. Expand the new project in the VS Solution Explorer.
    1. Right-click Dependencies and, in the context menu, select Manage Connected Services.
    2. On the Connected Services tab, click Add a service reference and choose gRPC.
    3. In the Add Service Reference dialog, choose the URL option and input the http version of the service address (remember to grab the randomly generated port number from launchsettings.json).
    4. Click Finish to add a service reference that can be easily maintained.

Feel free to check your work against the sample code for this example. Since, under the hood, VS has generated the same client we used in our first round of testing, we can reuse the contents of the Program.cs file from the previous service verbatim.

When we change a contract, we need to modify our client gRPC definition to match the updated .proto definition. To do so, we need only access VS’s Connected Services and refresh the relevant service entry. Now, our gRPC project is complete, and it’s easy to keep our service and client in sync.

Your Next Project Candidate: gRPC

Our gRPC implementation provides a firsthand glimpse into the benefits of using gRPC. REST and gRPC each have their own ideal use cases depending on contract type. However, when both options fit, I encourage you to try gRPC—it’ll put you ahead of the curve in the future of APIs.

Understanding the basics

gRPC is a framework to provide communication between systems.

REST APIs generally use JSON or XML message formats, while gRPC uses protocol buffers. To signal errors, REST APIs use HTTP status codes, while gRPC uses error codes. gRPC’s message sizes tend to be dramatically smaller than those of REST APIs.

Protocol buffers are gRPC’s format for contract messages. The gRPC library uses this format as its serialization structure.

When we compare gRPC to REST performance, gRPC is notably faster than REST.

gRPC-Web is a browser-side JavaScript client for communicating with gRPC services.

Comments

Thiago Dias
I can see the advantages of having a strong "contract based" tool such as gRPC, but the performance benefits could be used in REST as well, we could use REST with Protobuf/Avro on HTTP2, i know that gRPC being a framework/library encapsulates lots of technical stuff for us in order to gain performance, but i feel that the ultimate difference between them are still "architectural", in terms of resources managing (REST) vs actions (RPC, without the G), does that make sense?
Georges.D
Good article ! have you try to deploy a grpc service on Azure App Service ?
Gurami Dagundaridze
Nice article, thanks for posting!
Lev Yastrebov
Thank you for the overview! Exactly what I've needed.
Ruben Ruvalcaba
Great article, very interesting alternative to rest. And great to see you're explaining it on VS
Alexandre Shimono
Could you name some frameworks to use with gRPC? My main interests are for Python, Java and Javascript. One big barrier gRPC will still face is the gain in productivity the existing frameworks provide to REST systems.
Dhani Aristyawan
nice article
Laszlo Gyori
gRPC doesn't require a framework per se, it's rather just some packages / the protoc compiler. Productivity can suffer, especially when new to gRPC, but the benefits of having continuous better performance general outweigh this in the long run. For more info on specific languages you can check out the official docs. https://grpc.io/docs/languages/
Laszlo Gyori
I get your point, REST in this sense is a very broad category, but when making the comparison the article it mostly refers to more traditional approach. In theory you could indeed implement your suggestion, but in practice it probably adds a lot of overhead. For gRPC the tools/packages are already there, but for REST less so. And even if, not sure what's the benefit there.
Laszlo Gyori
For what it's worth, I have used gRPC only on AWS so far, but as far as I am aware should work there as well with minimal effort.
Laszlo Gyori
Hi! First of all, sorry for the late reply. I understand your concerns, but note this is a high-level comparison. On lower level it indeed starts to fall apart, mainly because we are comparing a theoretical standard to an actual technology. REST is not one specific technology, more a set of guidelines which should, but not must, be respected. If you have a fully RESTful API for an online store, but then decide to add an endpoint /updateOrderAndCustomerAddress which works with POST, is it not a REST API anymore? It clearly violates REST, but has no practical consequences. What I am getting here at is that there is no one exact answer on what is a REST API and what isn't. The article also does not aim to decide this, instead it takes a theoretical "average" API and for practical purposes ignores outliers (like REST with protobuf), or things not strictly related to REST. For example the compression you mention is totally unrelated to REST, it is a client-server communication feature, regardless of the endpoint type (REST, SOAP, etc.) So please treat this article as a more pragmatic approach to evaluating alternatives. As an example let's say you write an article titled Angular vs. React, where you compare the two. Now any senior front-end developer could be telling you the article is flawed, it compares an opinionated framework with a rendering library. Yet an architect or project manager could use that resource to help decide which one to choose for the next project.
comments powered by Disqus