When we talk about APIs, 90% of people are referring to a REST API which nowadays is the most common and popular way to connect applications, whether from the user interface, a third-party application, or another system.
But REST isn’t the only way; previously we looked at GraphQL and its features, and today, we’re going to see another way to communicate: gRPC.
Table of Contents
1 - What is gRPC?
According to the official description (on their website), gRPC is a universal open-source framework for high-performance RPC.
Let’s break down this phrase a bit:
- We say it’s high-performance because of the way we serialize and deserialize communication or calls.
- It’s called a universal framework because it’s language-agnostic, working with C#, Java, C++, Python, etc.
- RPC stands for Remote Procedure Call, which means a service or program will request another to perform some operation. It's the equivalent of a request in the REST world that you're probably used to.
And the "g"? Well, if you check their website, they'll say it's like the L in Linux, that the g stands for gRPC. Actually, though it isn’t confirmed, the g probably stands for Google because they created it.
1.1 - Features of gRPC
When we work with gRPC, we’ll be dealing with binary communication. Unlike Rest or even GraphQL, where we send text, here we send binary data, which means the packages being transmitted over the network are as small as possible.
Communication in gRPC is based on contracts. What do I mean by this? Very simply: for serialization and deserialization to work properly, both the client and server need to have the same contract; otherwise, deserialization will fail.
These contracts are the gRPC definition files, which have the .proto extension. You need to keep them updated when any change happens.
HTTPS is required, gRPC works only over secure calls using HTTPS. You can add authentication, though it’s optional; the mandatory part is HTTPS.
Note: It works over HTTP/2.
Bidirectional streaming: If you’re used to REST, everything works as Request/Response. gRPC can send and receive data simultaneously; for example, there’s no need for a request to finish before sending data back.
A simple example: in a video call, all participants are sending and receiving data simultaneously.
2 - Where to use gRPC?
Based on the first point, we might assume we should use gRPC everywhere, right? It's faster and lighter than any other alternative, so it would make sense.
But that's not the reality. The reasons are simple: on the frontend or in JavaScript, it’s not as easy to work with gRPC as with alternatives. So most leave it aside, maintaining the contract from the frontend isn’t appealing to many, because when they make a call, they can't see with their own eyes what is being sent to the server while developing 😅.
Also, serializing to binary is more expensive than to JSON, which, considering many of our clients may use computers from 15 or 20 years ago, just isn’t worth it.
In my opinion, communication between the frontend and backend should be done with REST/GraphQL.
Where gRPC shines is in system-to-system communication.
If you have one API calling another API or service, that’s where gRPC shines and does a great job.

In summary, anywhere the user does not have access.
As you can see in that image, there’s a call between the orders API and the products API. You may be thinking: if the user has access, we use REST or GraphQL, right? Yes and no. We use REST for user-facing parts, but if we need to call something from another API, we can use gRPC.
Note: In this image we have a fully coupled system, which in other words is called a distributed monolith. If you want to implement distributed systems properly, check out my free course on distributed systems.
When an API is both public to users and used internally like in the example, you shouldn’t always do gRPC, you need to evaluate development and maintenance costs versus the benefits. The key point is that both systems, a REST API and gRPC, can absolutely coexist in the same application.
As an extra, LinkedIn has an article on their engineering blog about how they migrated from REST to gRPC and improved latency (response time) by 60%.
As a side effect of saving 60% on latency, you also save on costs, not just on bandwidth but also CPU, memory, etc., which in today’s serverless world is very important.
3 - Contracts in gRPC
Contracts in gRPC deserve their own section, even though we could put them in the implementation section as they’re language-agnostic, I think it’s important to separate them.
Here’s an example of a contract you find in the C# gRPC template:
syntax = "proto3";
option csharp_namespace = "GrpcService1";
package greet;
// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply);
}
// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}
// The response message containing the greetings.
message HelloReply {
  string message = 1;
}This is a contract, in other words, a protobuf file, which defines the interface (hence the name "contract") that both the server and the client will use, and this file is completely language-independent.
Now let’s break down this file.
- The first line defines the schema used by the file, in our current case, proto3, which is basically the version. You don’t need to change it.
- The next line, option csharp_namespace = "GrpcService1";, defines the namespace where the code generated from this file will be located in your code. Normally you'll choose a namespace within your server. This is specific to C#. Other languages can use different options: in Ruby, for example, it’sruby_package GrpcService1.
- The package greetline is for organizing protobuf. It has nothing to do with the language implementing protobuf.
- The next section defines the service and the methods or functions that this file will generate and support:
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply);
}
// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}
// The response message containing the greetings.
message HelloReply {
  string message = 1;
}As you can see, it includes both the request and the response, with their definitions below.
- The numbers =1that you see in the messages identify each element that will be used later in serialization/deserialization. If you have several properties, just put them in order; there's no sense in skipping numbers.
4 - Implementing gRPC in C#
In today’s post, we’ll see how to implement both the client and server side using C#.
4.1 - Implementing a gRPC Server in C#
Here, we’ll look at the server side of gRPC using the default template project from Visual Studio or Rider. You can add gRPC to any existing project without issue. Anyway, let’s cover all we need.
First, you’ll need the NuGet package Grpc.AspnetCore.
Once that’s done, create your proto file; in the template this is greet.proto that we explained in the previous section, and it usually goes in a folder called protos.
But this alone isn’t enough to do anything. You can’t directly reference a .proto file. That’s where the package comes in: now, when you build, a C# file will be generated containing the types and methods you included in the proto file.

This file is autogenerated, while you technically can edit it, don't, because the next build will overwrite your changes.
I haven’t said this before, but this file works a bit differently from the others because it has its own tag in the csproj file. This is important because in that tag you define whether your app will be a server or client:
<ItemGroup>
    <Protobuf Include="Protos\greet.proto" GrpcServices="Server"/>
</ItemGroup>
Once that’s clear, the C# file generated automatically contains not only the types and methods. The methods are inside an abstract class called GreeterBase, named after the service in our proto file.
Importantly, this class has all the methods we specified, but their implementation throws exceptions:
/// <summary>Base class for server-side implementations of Greeter</summary>
[grpc::BindServiceMethod(typeof(Greeter), "BindService")]
public abstract partial class GreeterBase
{
    /// <summary>
    /// Sends a greeting
    /// </summary>
    /// <param name="request">The request received from the client.</param>
    /// <param name="context">The context of the server-side call handler being invoked.</param>
    /// <returns>The response to send back to the client (wrapped by a task).</returns>
    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
    public virtual global::System.Threading.Tasks.Task<global::GrpcService1.HelloReply> SayHello(global::GrpcService1.HelloRequest request, grpc::ServerCallContext context)
    {
    throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
    }
}
But that’s nothing to worry about, the point is to create a class that implements this abstract class and its methods. In that class, you can inject any services you want or perform needed operations. In short, this is the entry point to your app when using gRPC.
public class GreeterService : Greeter.GreeterBase
{
    private readonly ILogger<GreeterService> _logger;
    public GreeterService(ILogger<GreeterService> logger)
    {
        _logger = logger;
    }
    public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
    {
        return Task.FromResult(new HelloReply
        {
            Message = "Hello " + request.Name
        });
    }
}
And like almost every functionality we use in .NET, we must add gRPC to the dependency container and include the service in the request pipeline:
builder.Services.AddGrpc();
...
app.MapGrpcService<GreeterService>();
Now our application is ready to receive calls, but not just any calls, only gRPC calls, because only Http2 is enabled in the configuration:
"Kestrel": {
    "EndpointDefaults": {
        "Protocols": "Http2"
    }
}
If you want to receive calls in a "normal" REST API, just change the protocol to Http1AndHttp2 so your API will support both gRPC and standard HTTP calls.
4.2 - Consuming a gRPC Service from C#
As a consumer, we can use any type of project, a C# one or any language, since what we’ll do is use the proto file created in the server app.
As we know from before, the proto file is known to both the server (who creates it) and the client application. In our case, we'll create an API with an endpoint that will simply call our gRPC service we just created.
For now, we’ll just create an endpoint called get-greetings, where we'll make the call:
app.MapGet("/get-greetings", ()=> "TODO");
To prep our code, we need two things.
First, install several NuGet packages:
- Grpc.Net.Client
- Google.Protobuf
- Grpc.Tools
Next, copy the proto file from the server app to this new app and make sure in the csproj it’s marked as Client:
<ItemGroup>
    <Protobuf Include="Protos\greet.proto" GrpcServices="Client" />
</ItemGroup>This step is important so you have access to the information generated by the build. Just as in the server it creates a base service to add your logic, on the client, it creates a client automatically.
To use this client in our endpoint, do several steps: first, create a GrpcChannel which contains the URL where our service is deployed.
With that channel, use the code generated from our proto file to instantiate the client, and finally call the gRPC client method you want to invoke:
app.MapGet("/get-greetings", () =>
{
    GrpcChannel channel = GrpcChannel.ForAddress("https://localhost:7171");
    Greeter.GreeterClient client = new Greeter.GreeterClient(channel);
    HelloReply reply = client.SayHello(new HelloRequest { Name = "example name" });
    return reply.Message;
});
And that’s it (as long as you do the next blog step, 4.2.1):

4.2.1 - Testing gRPC locally with certificates
Earlier I mentioned that gRPC only works over https, but not only that, when using HTTP/2, you need TLS enabled, which is a hassle because you need a valid certificate.
There are two ways to solve this locally. First, you can create an HTTP handler that skips certificate validation:
var handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = 
    HttpClientHandler.DangerousAcceptAnyServerCertificateValidator; 👈
var channel = GrpcChannel.ForAddress("https://localhost:7171",
    new GrpcChannelOptions { HttpHandler = handler }); 👈
var client = new Greet.GreeterClient(channel); Obviously, this is a bit risky in case you forget to remove it before going to production.
The second is to trust the local dotnet certificate, done by running this command:
dotnet dev-certs https --trustThis is the approach we should use when working locally, in my opinion.
4.2.2 - Adding gRPC to the dependency container
Our endpoint is a bit ugly, instantiating the channel and then the client, honestly, it doesn’t look very elegant. What we can do is add the client to the dependency container. For this, just add gRPC to the container, then add the client:
builder.Services.AddGrpc();
builder.Services.AddGrpcClient<Greeter.GreeterClient>(x =>
{
    x.Address = new Uri("https://localhost:7171");
});
And in the endpoint (or use case), just inject the client:
app.MapGet("/get-greetings", (Greeter.GreeterClient client) =>
{
    HelloReply reply = client.SayHello(new HelloRequest { Name = "example name" });
    return reply.Message;
});
Notice that when we added the client to the dependency container we used AddGrpcClient<T>, which means you can have as many clients as you want.
If there is any problem you can add a comment bellow or contact me in the website's contact form
 
                    