blog
Ottorino Bruni  

Getting Started with gRPC in ASP.NET Core with a Real Example

Introduction

When building modern backend applications, communication between services is a key aspect of system design. Most developers are familiar with REST APIs, which have been the standard approach for years. However, as systems grow and performance becomes more critical, alternative solutions start to gain attention.

One of these solutions is gRPC, a high-performance Remote Procedure Call (RPC) framework originally developed by Google. Instead of working with traditional HTTP endpoints and JSON, gRPC allows services to communicate by calling methods directly, using a strongly typed contract defined in Protocol Buffers.

The concept behind gRPC is based on Remote Procedure Call, where a client can execute a function on a remote server as if it were a local method. This idea has been around for decades and is well documented in Remote Procedure Call, but gRPC brings it into modern architectures with support for HTTP/2, efficient binary serialization, and streaming.

In the .NET ecosystem, gRPC is fully supported in ASP.NET Core and is becoming an important option for building fast, scalable, and reliable services, especially in microservices architectures.

In this article, we will explore how gRPC works in .NET, when to use it, and how to build a simple real-world example step by step.

What is gRPC in Simple Terms

gRPC is a framework that allows applications to communicate by calling methods on a remote server, as if they were local functions.

It is based on the concept of Remote Procedure Call and uses Protocol Buffers instead of JSON, making communication faster and more efficient.

In simple terms:

  • With REST β†’ you call URLs and exchange JSON
  • With gRPC β†’ you call methods and exchange binary data

This makes gRPC particularly useful when performance, low latency, and strong typing are important.

gRPC vs REST in .NET

When building APIs in .NET, REST is still the most common choice. However, gRPC offers a different approach that can be more efficient in certain scenarios.

The main difference is how communication happens:

  • REST is based on HTTP endpoints (URLs) and typically uses JSON
  • gRPC is based on method calls and uses Protocol Buffers (binary format)

Here is a simple comparison:

FeatureRESTgRPC
CommunicationHTTP (mostly HTTP/1.1)HTTP/2
Data FormatJSON (text-based)Protocol Buffers (binary)
PerformanceGoodHigh (faster, smaller payload)
TypingLooseStrong (contract-first)
StreamingLimitedBuilt-in (bi-directional)

Why Use gRPC Instead of REST (JSON)?

At first glance, REST APIs using JSON are simple and widely adopted. So why should you consider gRPC?

The main reason is performance and efficiency.

gRPC uses Protocol Buffers instead of JSON, which is a compact binary format. This means:

  • Smaller payloads β†’ less data transferred over the network
  • Faster serialization β†’ better performance
  • Lower latency β†’ especially important in service-to-service communication

Another key advantage is strong typing.

With REST, you manually define models and map JSON data. With gRPC, the .proto file defines a strict contract, and C# classes are generated automatically. This reduces errors and improves maintainability.

When NOT to Use gRPC

While gRPC is a powerful technology, it is not always the best choice.

There are scenarios where REST APIs using JSON are still more appropriate:

  • Public APIs
    If your API is consumed by external developers, REST is easier to use, test, and integrate.
  • Browser-based applications
    Browsers do not natively support gRPC (only gRPC-Web with limitations), so REST is usually a better fit.
  • Simple CRUD applications
    For basic operations where performance is not critical, REST is simpler and faster to implement.

A good rule of thumb is:

If humans or browsers consume your API β†’ REST
If services talk to each other β†’ gRPC

In many real-world systems, both are used together: REST for external APIs and gRPC for internal communication between services.

How gRPC Works (High-Level)

At a high level, gRPC works by defining a contract between client and server, then generating code to handle communication automatically.

Here is the flow in simple terms:

Getting Started with gRPC in ASP.NET Core – How gRPC works
  1. Define the contract (.proto file)
    You describe your service and methods using Protocol Buffers. This acts as a shared agreement between client and server.
  2. Generate code
    From the .proto file, gRPC generates strongly typed classes for both client and server in C#.
  3. Client calls a method
    Instead of calling a URL, the client calls a method like:await client.GetStudentAsync(request);
  4. Serialization with Protocol Buffers
    The request is converted into a compact binary format (much smaller than JSON).
  5. Communication over HTTP/2
    The data is sent over HTTP/2, which supports:
    • Multiplexing (multiple requests in parallel)
    • Streaming (real-time data flow)
    • Lower latency
  6. Server processes the request
    The server receives the request, executes the method, and returns a response.
  7. Client receives a typed response
    The response is automatically deserialized into a C# object.
Client                Network (HTTP/2)              Server
  |                         |                         |
  | Call Method             |                         |
  |------------------------>|                         |
  |   Serialize (protobuf)  |                         |
  |                         |------------------------>|
  |                         |   Execute Method        |
  |                         |<------------------------|
  |   Deserialize Response  |                         |
  |<------------------------|                         |

Key Idea

gRPC hides the complexity of network communication and lets you work with strongly typed methods instead of raw HTTP requests.

This is why it feels like calling a local function, even though everything happens over the network.

Creating a Simple gRPC Service in ASP.NET Core

To better understand how gRPC works in .NET, we will build a simple example step by step.

Instead of creating a full application upfront, we will gradually introduce each part of a gRPC service. We will start with the contract definition, then move to the server implementation, and finally see how a client can call the service.

This approach keeps things simple and helps you clearly understand how each component fits together.

In the following sections, we will use a basic example based on a Student service to make everything more concrete and easy to follow.

Defining the .proto File

In gRPC, the .proto file is one of the most important parts of the application because it defines the contract between client and server.

You can think of it as a shared agreement that describes:

  • which methods are available
  • what data the client sends
  • what data the server returns

This contract-first approach is one of the reasons gRPC feels strongly typed and structured compared to a typical REST API.

For our example, we can define a simple service that returns a student by id.

syntax = "proto3";

option csharp_namespace = "GrpcStudentService";

package student;

service StudentService {
  rpc GetStudent (StudentRequest) returns (StudentReply);
}

message StudentRequest {
  int32 id = 1;
}

message StudentReply {
  int32 id = 1;
  string name = 2;
  int32 age = 3;
}

Let’s break this down:

  • syntax = “proto3”; tells gRPC which Protocol Buffers version we are using
  • option csharp_namespace defines the namespace for the generated C# classes
  • service StudentService declares the service
  • rpc GetStudent defines a method that accepts a StudentRequest and returns a StudentReply
  • message defines the structure of the request and response data

One important thing to notice is that each field has a number:

int32 id = 1;
string name = 2;
int32 age = 3;

These numbers are used internally by Protocol Buffers during serialization, which helps keep messages compact and efficient.

Once this file is added to the project, .NET can generate the C# types needed for both the server and the client.

Implementing the gRPC Server

Now that we have defined the contract in the .proto file, the next step is to implement the server.

When you build the project, .NET automatically generates C# classes from the .proto file. One of these is a base class for your service, which you can extend to provide your own logic.

For our example, we implement the StudentService like this:

using Grpc.Core;

public class StudentServiceImpl : StudentService.StudentServiceBase
{
    public override Task<StudentReply> GetStudent(StudentRequest request, ServerCallContext context)
    {
        var student = new StudentReply
        {
            Id = request.Id,
            Name = "Otto",
            Age = 22
        };

        return Task.FromResult(student);
    }
}

Let’s break it down:

  • StudentServiceBase is the generated base class from the .proto file
  • GetStudent is the method we defined earlier
  • request contains the data sent by the client
  • StudentReply is the response object returned to the client

In this simple example, we are just returning static data, but in a real application this is where you would:

  • call a database
  • access external services
  • apply business logic

Registering the Service

To make the service available, you need to register it in Program.cs:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddGrpc();

var app = builder.Build();

app.MapGrpcService<StudentServiceImpl>();

app.Run();

This tells ASP.NET Core to expose your gRPC service so that clients can call it.


Key Idea

The server implementation is just normal C# code. gRPC takes care of serialization, networking, and communication for you.

Common Issues When Running a gRPC Client and Server

When trying this example for the first time, you may run into a few common problems.

One common issue is using the wrong server address or port. The client must connect to the same address exposed by the gRPC server. If the port is different, the call will fail.

Another common problem is related to HTTPS and the development certificate. Since gRPC in ASP.NET Core typically runs over HTTPS, the client may fail if the local development certificate is missing or not trusted.

You can fix this with:

dotnet dev-certs https --trust

You may also see errors if the .proto file is not correctly included in the client project. In that case, the generated C# classes will not be available, and the client code will not compile.

Finally, remember that gRPC is designed to work over HTTP/2. If the environment is not configured correctly for HTTP/2, communication may not work as expected.

If you are just getting started, do not worry too much about these details at first. Most problems usually come from configuration, not from the gRPC code itself.

Practical Example: Building a Simple gRPC Student Service

Disclaimer: This example is purely for educational purposes. There are better ways to write code and applications that can optimize this example. Use this as a starting point for learning, but always strive to follow best practices and improve your implementation.

A common use case for gRPC is fast and efficient communication between services. In this example, we will build a complete solution composed of a server, a client and a shared contracts project.

The shared contracts project will contain the .proto file, which acts as the single source of truth for both the client and the server. This approach avoids duplication and ensures consistency across the application.

We will implement a simple Student service that allows a client to request student data from the server using gRPC.

Source Code

The complete source code for this example is available on GitHub:

πŸ‘‰ https://github.com/ottorinobruni/GrpcStudentSolution?ref=ottorinobruni.com

Feel free to explore the project, run it locally, and use it as a starting point for your own gRPC applications in .NET.

Prerequisites

Before starting, make sure you have the following installed:

  • .NET SDK: Download and install the .NET SDK if you haven’t already.
  • Visual Studio Code (VSCode): Install Visual Studio Code for a lightweight code editor.
  • C# Dev Kit for VSCode: Install the C# Dev Kit for VSCode to enable C# support.

Verify your installation:

dotnet --version

Step 1 – Create the Solution

Open your terminal and create a new solution to hold all projects

mkdir GrpcStudentSolution
cd GrpcStudentSolution
dotnet new sln -n GrpcStudentSolution
cd GrpcStudentSolution
code .

Step 2 – Create the Projects

Create server, client, and shared contracts:

dotnet new grpc -n GrpcStudentService
dotnet new console -n GrpcStudentClient
dotnet new classlib -n GrpcContracts

Add them to the solution:

dotnet sln add GrpcStudentService
dotnet sln add GrpcStudentClient
dotnet sln add GrpcContracts

Step 3 – Define the Shared Contract

Create a Protos folder inside GrpcContracts and add student.proto:

syntax = "proto3";

option csharp_namespace = "GrpcContracts";

package student;

service StudentService {
  rpc GetStudent (StudentRequest) returns (StudentReply);
}

message StudentRequest {
  int32 id = 1;
}

message StudentReply {
  int32 id = 1;
  string name = 2;
  int32 age = 3;
}

Step 4 – Configure the Contracts Project

Update GrpcContracts.csproj:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <Protobuf Include="Protos\student.proto" GrpcServices="Both" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Google.Protobuf" Version="*" />
    <PackageReference Include="Grpc.AspNetCore" Version="*" />
    <PackageReference Include="Grpc.Tools" Version="*" PrivateAssets="All" />
  </ItemGroup>

</Project>

This enables automatic code generation for both client and server.

Step 5 – Add Project References

dotnet add GrpcStudentService reference GrpcContracts
dotnet add GrpcStudentClient reference GrpcContracts

dotnet add GrpcStudentClient package Grpc.Net.Client

Step 6 – Implement the Server

Create StudentServiceImpl:

using Grpc.Core;
using GrpcContracts;

public class StudentServiceImpl : StudentService.StudentServiceBase
{
    public override Task<StudentReply> GetStudent(StudentRequest request, ServerCallContext context)
    {
        return Task.FromResult(new StudentReply
        {
            Id = request.Id,
            Name = "Otto",
            Age = 22
        });
    }
}

Register it in Program.cs:

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddGrpc();

var app = builder.Build();

// Configure the HTTP request pipeline.
app.MapGrpcService<StudentServiceImpl>();

app.MapGet("/", () => "Communication with gRPC endpoints");

app.Run();

Step 7 – Configure HTTP/2 for Local Development

gRPC requires HTTP/2. For local development, configure the server to use HTTP without TLS.

This configuration is only for development. In production, always use HTTPS.

Create appsettings.Development.json:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "Kestrel": {
    "EndpointDefaults": {
      "Protocols": "Http2"
    }
  }
}

Update launchSettings.json:

{
  "$schema": "https://json.schemastore.org/launchsettings.json",
  "profiles": {
    "http": {
      "commandName": "Project",
      "dotnetRunMessages": true,
      "launchBrowser": false,
      "applicationUrl": "http://localhost:5000",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }
  }
}

Step 8 – Implement the Client

Update Program.cs:

using Grpc.Net.Client;
using GrpcContracts;

// This line enables HTTP/2 without HTTPS (TLS).
AppContext.SetSwitch(
    "System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);

using var channel = GrpcChannel.ForAddress("http://localhost:5000");

var client = new StudentService.StudentServiceClient(channel);

var response = await client.GetStudentAsync(new StudentRequest { Id = 1 });

Console.WriteLine($"Student: {response.Name}, Age: {response.Age}");

Step 9 – Run the Application

Start the server:

cd GrpcStudentService
dotnet run

Run the client:

cd GrpcStudentClient
dotnet run

Expected output:

Student: Otto, Age: 22
Getting Started with gRPC in ASP.NET Core – Run Example

Key Takeaway

Using a shared contracts project ensures consistency between client and server and avoids duplication of .proto files.

Conclusion

gRPC is a powerful option in ASP.NET Core when you need fast, strongly typed communication between applications or services.

Compared to REST, it offers better performance, smaller payloads, and a contract-first approach based on Protocol Buffers. While REST is still a great choice for public APIs and browser-friendly scenarios, gRPC is often a better fit for internal services and microservice communication.

In this article, we looked at the basic concepts behind gRPC, how it differs from REST, how it works at a high level, and how to build a simple client-server example in .NET.

If you have never used gRPC before, the best next step is to experiment with a small real project and get familiar with .proto files, generated code, and service-to-service communication in ASP.NET Core.

If you think your friends or network would find this article useful, please consider sharing it with them. Your support is greatly appreciated.

Thanks for reading!

Discover CodeSwissKnife Bar, your all-in-one, offline Developer Tools from Your Menu Bar

Leave A Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.