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:
| Feature | REST | gRPC |
|---|---|---|
| Communication | HTTP (mostly HTTP/1.1) | HTTP/2 |
| Data Format | JSON (text-based) | Protocol Buffers (binary) |
| Performance | Good | High (faster, smaller payload) |
| Typing | Loose | Strong (contract-first) |
| Streaming | Limited | Built-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:

- Define the contract (.proto file)
You describe your service and methods using Protocol Buffers. This acts as a shared agreement between client and server. - Generate code
From the.protofile, gRPC generates strongly typed classes for both client and server in C#. - Client calls a method
Instead of calling a URL, the client calls a method like:await client.GetStudentAsync(request); - Serialization with Protocol Buffers
The request is converted into a compact binary format (much smaller than JSON). - 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
- Server processes the request
The server receives the request, executes the method, and returns a response. - 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
StudentRequestand returns aStudentReply - 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:
StudentServiceBaseis the generated base class from the.protofileGetStudentis the method we defined earlierrequestcontains the data sent by the clientStudentReplyis 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

Key Takeaway
Using a shared contracts project ensures consistency between client and server and avoids duplication of
.protofiles.
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
