Build a gRPC Microservice with ASP.NET Core 10 and Protocol Buffers

by Didin J. on Sep 23, 2025 Build a gRPC Microservice with ASP.NET Core 10 and Protocol Buffers

Learn how to build a gRPC microservice with ASP.NET Core 10 and Protocol Buffers. Step-by-step guide with setup, implementation, and best practices.

In today’s world of distributed systems and microservices, communication between services needs to be fast, efficient, and reliable. While REST APIs are still widely used, more and more developers are turning to gRPC — a modern, high-performance Remote Procedure Call (RPC) framework that uses HTTP/2 for transport and Protocol Buffers (Protobuf) for message serialization.

Unlike traditional REST, where data is typically exchanged in JSON format over HTTP/1.1, gRPC offers several key advantages:

  • Smaller and faster messages — thanks to Protocol Buffers’ binary serialization.

  • Built-in support for streaming — including server streaming, client streaming, and bidirectional streaming.

  • Strongly-typed contracts — generated from .proto files, reducing serialization errors.

  • Cross-platform and multi-language support — gRPC clients and servers can be built in C#, Java, Go, Python, Node.js, and more.

  • Better performance — leveraging HTTP/2 multiplexing and compression.

In this tutorial, we’ll walk step by step through building a gRPC microservice with ASP.NET Core 10 and Protocol Buffers. You’ll learn how to:

  • Create a gRPC service with the latest ASP.NET Core 10 project template.

  • Define Protobuf message contracts and service definitions.

  • Implement server-side logic using strongly typed C# classes.

  • Build a gRPC client to call the service.

  • Containerize the microservice with Docker and deploy it to Kubernetes.

  • Apply best practices like versioning, interceptors, and observability.

By the end, you’ll have a working microservice that can be expanded into a larger system — whether you’re building internal APIs, real-time communication services, or high-performance backend integrations.


Prerequisites and Project Setup

Before diving into code, make sure your environment is ready for ASP.NET Core 10 gRPC development.

1. Prerequisites

You’ll need the following installed on your system:

  • .NET 10 SDK – download from the .NET website.

  • Visual Studio 2022 (v17.10 or later), Visual Studio Code, or Rider – choose your favorite IDE.

  • Docker Desktop – if you plan to containerize and run locally.

  • Kubernetes cluster (minikube, k3d, Docker Desktop, or cloud provider) – optional, for deployment.

  • cURL or gRPCurl – for quick testing of gRPC endpoints.

  • Protocol Buffers Compiler (protoc) – although .NET’s tooling generates code automatically, having protoc installed is useful when working across multiple languages.

Check your installations

dotnet --version
# should print something like 10.0.100-preview.7.25380.108
docker --version
kubectl version --client

2. Create a New gRPC Project

ASP.NET Core provides a built-in gRPC project template. To create a new project, run:

dotnet new grpc -o GrpcService
cd GrpcService

This will generate a ready-to-run gRPC service with a sample Greeter service and .proto file. The structure looks like this:

GrpcService/
├─ Protos/
│  └─ greet.proto
├─ Services/
│  └─ GreeterService.cs
├─ appsettings.json
├─ Program.cs
├─ GrpcService.csproj

Key files explained

  • Protos/greet.proto → Defines your service contracts and messages.

  • Services/GreeterService.cs → Implements the service logic generated from the .proto.

  • Program.cs → Configures gRPC and starts the app.

3. Run the Project

Let’s test the generated project before making changes.

dotnet run

By default, the gRPC service runs on https://localhost:5070 (HTTP/2). If you open that URL in your browser, you’ll see a plain text message:

gRPC service running. Use a gRPC client to call the endpoints.

That means your gRPC server is up and running 🎉


Defining the Protocol Buffers Contract

At the heart of any gRPC service is the Protocol Buffers (.proto) file, which defines the service contracts and the data structures (messages) exchanged between client and server. From this single file, strongly typed code is automatically generated in multiple languages, ensuring consistency across platforms.

1. What is a .proto file?

A .proto file describes:

  • Messages: The data objects exchanged between client and server.

  • Services: The RPC methods exposed by the server.

  • Options: Such as namespaces, package names, or code generation settings.

Here’s the general structure:

syntax = "proto3";

option csharp_namespace = "GrpcDemo.Protos";

package myservice;

service MyService {
  rpc MyMethod (MyRequest) returns (MyResponse);
}

message MyRequest {
  string name = 1;
}

message MyResponse {
  string message = 1;
}

2. Creating a New .proto File

In the Protos/ folder of your project, create a new file called greeting.proto:

syntax = "proto3";

option csharp_namespace = "GrpcService.Protos";

package greeting;

service Greeting {
  // Unary RPC
  rpc SayHello (HelloRequest) returns (HelloReply);

  // Server streaming RPC
  rpc SayHelloStream (HelloRequest) returns (stream HelloReply);
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

Key points:

  • syntax = "proto3"; → We’re using Protocol Buffers v3.

  • option csharp_namespace = "GrpcService.Protos"; → Ensures generated C# classes go into this namespace.

  • package greeting; → Organizes services/messages for versioning and multi-language clients.

  • The SayHello method is a unary RPC (one request → one response).

  • The SayHelloStream method is a server streaming RPC (one request → multiple responses).

3. Update GrpcService.csproj

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>
  <ItemGroup>
    <Protobuf Include="Protos\greeting.proto" GrpcServices="Server" />
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include="Grpc.AspNetCore" Version="2.64.0" />
    <PackageReference Include="Grpc.Tools" Version="2.64.0">
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
  </ItemGroup>
</Project>

4. Rebuild Project

dotnet clean
dotnet build

5. How Code Generation Works

When you build your project, the gRPC tooling in .NET automatically:

  • Compiles .proto files.

  • Generates strongly typed C# classes for messages (HelloRequest, HelloReply).

  • Generates a base service class (Greeting.GreetingBase) that you extend on the server.

  • Generates a client (Greeting.GreetingClient) for consuming the service.

This means you don’t manually write serialization logic — the Protobuf compiler handles it for you.

✅ At this point, your service contract is defined. Next, we’ll implement the server logic in C# by extending the generated base class.


Implementing the gRPC Service in ASP.NET Core 10

With our greeting.proto file defined, the next step is to implement the server-side logic that will handle incoming gRPC calls. When you build your project, .NET automatically generates C# classes from the .proto file, including:

  • Message classes: HelloRequest, HelloReply

  • Service base class: Greeting.GreetingBase

  • Client stub: Greeting.GreetingClient

We’ll now extend the base class to implement our methods.

1. Create the Service Class

Inside the Services/ folder, create a new file called GreetingService.cs:

using Grpc.Core;
using GrpcService.Protos;

namespace GrpcService.Services;

public class GreetingServiceImpl : GreetingService.GreetingServiceBase
{
    public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
    {
        return Task.FromResult(new HelloReply
        {
            Message = $"Hello {request.Name}"
        });
    }

    public override async Task SayHelloStream(
        HelloRequest request,
        IServerStreamWriter<HelloReply> responseStream,
        ServerCallContext context)
    {
        for (int i = 0; i < 5; i++)
        {
            await responseStream.WriteAsync(new HelloReply
            {
                Message = $"Hello {request.Name}, message {i + 1}"
            });

            await Task.Delay(1000);
        }
    }
}

Key Points:

  • SayHello → Implements a unary RPC that returns a single response.

  • SayHelloStream → Demonstrates a server streaming RPC, sending multiple responses back to the client.

  • Both methods inherit from Greeting.GreetingBase, which is generated from greeting.proto.

2. Register the Service in Program.cs

Open Program.cs and map the new service:

using GrpcService.Services;

var builder = WebApplication.CreateBuilder(args);

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

var app = builder.Build();

// Configure the HTTP request pipeline.
app.MapGrpcService<GreetingServiceImpl>();
app.MapGet("/", () => "gRPC service running. Use a gRPC client to call the endpoints.");

app.Run();

3. Run the Server

Rebuild and run the project:

dotnet run

Output:

info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:5070

Your gRPC service is now live and ready to handle requests for both unary and streaming calls.

✅ At this point, we have a working gRPC server with ASP.NET Core 10. Next, we’ll build a gRPC client to consume these endpoints.


Configuring gRPC Endpoints in Program.cs

With the protocol buffer contract defined and the GreetingServiceImpl implemented, the final step to expose your service is to configure it in Program.cs. This file is the entry point of your ASP.NET Core 10 application and is responsible for registering services and mapping routes.

Open Program.cs and update it as follows:

using GrpcService.Services; // Import the namespace of your implementation

var builder = WebApplication.CreateBuilder(args);

// Register gRPC services with the DI container
builder.Services.AddGrpc();

var app = builder.Build();

// Map the gRPC service to the request pipeline
app.MapGrpcService<GreetingServiceImpl>();

// Optional: Add a simple HTTP GET endpoint for health/status check
app.MapGet("/", () => "This server hosts a gRPC service. Use a gRPC client to communicate.");

app.Run();

Explanation

  1. builder.Services.AddGrpc()
    Registers gRPC support so that the ASP.NET Core runtime knows how to handle gRPC calls.

  2. app.MapGrpcService<GreetingServiceImpl>()
    Maps your service implementation (GreetingServiceImpl) to the request pipeline, making it available to clients.

  3. Fallback endpoint (app.MapGet("/"))
    Since gRPC clients cannot be tested directly in a browser, this small GET endpoint acts as a quick health/status check. If you navigate to http://localhost:5070, you’ll see the message:

    This server hosts a gRPC service. Use a gRPC client to communicate.

Running the Service

Start the application:

dotnet run

You should see logs like:

info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:5070
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: /Users/didin/ASPNETProjects/GrpcService

At this point, your gRPC service is fully hosted and ready to accept client requests.


Building a gRPC Client in ASP.NET Core

Now that your gRPC service is running, you’ll need a client application to consume it. We’ll create a separate ASP.NET Core Console App that connects to the server and calls the GreetingService methods.

Step 1: Create a Console App Project

In your workspace folder, run:

dotnet new console -n GrpcClient
cd GrpcClient

This will create a new console project named GrpcClient.

Step 2: Add Required Packages

Install the gRPC client libraries and tooling:

dotnet add package Grpc.Net.Client --version 2.64.0
dotnet add package Google.Protobuf --version 3.27.0
dotnet add package Grpc.Tools --version 2.64.0

These packages provide:

  • Grpc.Net.Client: gRPC client support for .NET.

  • Google.Protobuf: Protocol Buffers serialization.

  • Grpc.Tools: Code generation from .proto files.

Step 3: Share the .proto Contract

To ensure both client and server use the same contract, copy greeting.proto from the server project into a new folder inside the client project:

GrpcClient/
 └── Protos/
     └── greeting.proto

Then update it:

syntax = "proto3";

option csharp_namespace = "GrpcShared.Protos";

package greeting;

service GreetingService {
  rpc SayHello (HelloRequest) returns (HelloReply);
  rpc SayHelloStream (HelloRequest) returns (stream HelloReply);
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

Update GrpcClient.csproj to include it:

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

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

  <ItemGroup>
    <Protobuf Include="Protos\greeting.proto" GrpcServices="Client" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Grpc.Net.Client" Version="2.64.0" />
    <PackageReference Include="Google.Protobuf" Version="3.27.0" />
    <PackageReference Include="Grpc.Tools" Version="2.64.0" PrivateAssets="all" />
  </ItemGroup>

</Project>

Step 4: Implement the Client

Open Program.cs and replace its contents with:

using Grpc.Net.Client;
using GrpcShared.Protos;
using Grpc.Core;          // Needed for IAsyncStreamReader<T>

namespace GrpcService.GrpcClient;

class Program
{
    static async Task Main(string[] args)
    {
        using var channel = GrpcChannel.ForAddress("http://localhost:5070");
        var client = new GreetingService.GreetingServiceClient(channel);

        // Unary call
        var reply = await client.SayHelloAsync(new HelloRequest { Name = "Djamware" });
        Console.WriteLine("Unary Response: " + reply.Message);

        // Server streaming call
        using var call = client.SayHelloStream(new HelloRequest { Name = "Djamware" });
        await foreach (var streamReply in call.ResponseStream.ReadAllAsync())
        {
            Console.WriteLine("Stream Response: " + streamReply.Message);
        }
    }
}

Step 5: Run the Client

Open two terminals:

  1. Terminal 1 – run the server:

     
    cd GrpcService
    dotnet run

     

  2. Terminal 2 – run the client:

     
    cd GrpcClient
    dotnet run

     

Expected Output

Unary Response: Hello Djamware
Stream Response: Hello Djamware, message 1
Stream Response: Hello Djamware, message 2
Stream Response: Hello Djamware, message 3
Stream Response: Hello Djamware, message 4
Stream Response: Hello Djamware, message 5

This confirms that your gRPC client successfully connected to the server and consumed both unary and streaming RPCs.


Best Practices and Conclusion

Building a gRPC microservice with ASP.NET Core 10 and Protocol Buffers gives you a modern, high-performance foundation for service-to-service communication. To ensure your implementation remains maintainable, scalable, and production-ready, here are some best practices to follow:

🔑 Best Practices

  1. Organize Protobuf files in a shared project

    • Place .proto contracts in a dedicated shared library (e.g., GrpcShared) so both server and clients use the same generated code.

    • This prevents version drift and makes updating contracts easier.

  2. Version your gRPC services

    • Introduce service versioning in package names (package greeting.v1;) to avoid breaking changes when evolving APIs.

    • Provide backward compatibility when possible.

  3. Use async streaming carefully

    • Server streaming (like our SayHelloStream) is powerful, but ensure you manage cancellation tokens to stop unnecessary processing if clients disconnect.

  4. Secure your gRPC endpoints

    • Always use TLS/HTTPS in production.

    • Consider implementing JWT authentication or integrating with ASP.NET Core Identity for client authorization.

  5. Implement logging and observability

    • Use structured logging (Serilog, built-in ILogger) to monitor request/response activity.

    • Combine with OpenTelemetry for distributed tracing in microservice environments.

  6. Error handling

    • Translate exceptions into proper gRPC status codes (e.g., StatusCode.NotFound, StatusCode.PermissionDenied) rather than exposing raw exceptions.

    • This ensures clients receive predictable error responses.

  7. Performance considerations

    • Keep payloads small and avoid overusing nested messages in .proto files.

    • Use deadlines/timeouts on clients to prevent long-hanging requests.

✅ Conclusion

In this tutorial, you learned how to:

  • Set up an ASP.NET Core 10 gRPC project.

  • Define contracts with Protocol Buffers (.proto).

  • Implement server-side logic in a strongly-typed gRPC service.

  • Configure endpoints and run the gRPC host.

  • Build and test a gRPC client to consume the service.

gRPC provides strong typing, performance, and cross-platform support, making it an excellent choice for building microservices and cloud-native systems. By following best practices around security, versioning, and observability, you can confidently run gRPC services in production environments.

You can find the full source code on our GitHub.

That's just the basics. If you need more deep learning about ASP.NET Core, Angular, or related, you can take the following cheap course:

Thanks!