In this post, I want to show you how you can call gRPC services using server-side Blazor. I just want to say that I've only been experimenting with gRPC for a couple of days so I'm very much still learning, but it's been a great experience so far.

Before we get into any code I want to just explain what gRPC is, for those who've not heard of it before.

All of the code for this post is available on GitHub.

What is gRPC

gRPC is a fast and efficient open source remote procedure call (RPC) framework initially created by Google. It's an evolution of an internal RPC technology called "Stubby". It uses HTTP/2 as its transport protocol and a technology called Protocol Buffers, or protobuf, to describe interfaces and messages.

Currently, gRPC stands for gRPC Remote Procedure Call. I say currently as the 'g' stands for something different in every release. Previous definitions include, 'green', 'good', 'gentle' and 'gregarious'. If you're interested, you can see the full list on the main repo on GitHub.

So why use it? Simply put, it's really fast and highly scaleable. Because of this, unsurprisingly, gRPC has gained a lot of traction in microservices.

When comparing REST with gRPC, it does look more limited. With REST you're limited to 4 primary functions, GET, PUT, PATCH and DELETE. With gRPC you can define any kind of function be that synchronous, asynchronous, uni-direction or even streams. gRPC also requires much less boilerplate code than REST. With much of the code needed on the server and the client generated automatically from protobuf files.

As I said at the start, I'm still learning about gRPC so I'm sure there's loads more good stuff to discover. But for now let's move on and look at how we can setup a simple gRPC service and configure a Blazor client.

Getting Setup

Everything I'm going to be showing you requires the latest .NET Core 3 SDK and Visual Studio 2019 Preview.

Microsoft are actively contributing to the gRPC for .NET project and as of .NET Core 3 Preview 3, there is now a template for building gRPC services. You can find it by creating a new ASP.NET Core Web Application.

The Service

I'm going to use the default gRPC service template as is, for this post. I'm not going to go into detail about how it's setup. But I do want to focus on greet.proto file, in the Protos folder.

syntax = "proto3";

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 file defines the contract for the service. The Greeter service has a single interface, SayHello, which takes a HelloRequest and returns a HelloReply.

The message definition for HelloRequest specifies one field called name. While HelloReply specifies one called message. Fields are either scalar types or composite types.

In the example above, all fields are scalar types with a type, name and unique number. It's important that once defined, these unique numbers don't change as they're used to identify fields once in binary format.

The gRPC tooling uses this file to generate a service base class which is then used in the GreeterService in the Services folder.

The Client

Now the service is in place I'm going to add a Blazor server-side project to the solution. This is going to be the gRPC client.

To make things easier to test, I'm going to make a change to allow multiple startup projects for the solution. This is done by right clicking on the solution and selecting Set Startup Projects... Then setting both projects action to Start.

I'm going to start by making a copy of greet.proto from the server project and adding it to the client project.

Next I'm going to make some additions to the clients csproj file.

I'm going to include 3 package references for Google.Protobuf, Grpc.Core and Grpc.Tools. I'm also including an ItemGroup referencing the protobuf file so the tooling can generate the client code needed to communicate with the server. The final csproj file looks like this.

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

  <PropertyGroup>
    <TargetFramework>netcoreapp3.0</TargetFramework>
    <LangVersion>7.3</LangVersion>
  </PropertyGroup>

  <ItemGroup>
    <Protobuf Include="Protos\greet.proto" GrpcServices="Client" Generator="MSBuild:Compile"/>
    <Content Include="@(Protobuf)" />
    <None Remove="@(Protobuf)" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Google.Protobuf" Version="3.8.0-rc.1" />
    <PackageReference Include="Grpc.Core" Version="1.21.0" />
    <PackageReference Include="Grpc.Tools" Version="1.21.0">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
  </ItemGroup>

</Project>

I need to add a couple of using statements to my main _Imports.razor.

@using Greet
@using Grpc.Core

Finally, I'm going to replace the contents of the Index.razor page with the following code.

@page "/"

<h1>Hello, gRPC!</h1>

<button class="btn btn-primary" onclick="@SayHello">Say Hello</button>

<hr />

<p>@Greeting</p>

@functions {

    private string Greeting;

    async Task SayHello()
    {
        var channel = new Channel("localhost:50051", ChannelCredentials.Insecure);
        var client = new Greeter.GreeterClient(channel);

        var reply = await client.SayHelloAsync(new HelloRequest { Name = "Blazor gRPC Client" });
        Greeting = reply.Message;

        await channel.ShutdownAsync();
    }

}

All the magic is happening in the SayHello method.

I start by creating a gRPC channel to the server. Next, I create an instance of the GreeterClient. This class is created by the code gen tools using the greet.proto file.

With an instance of the client I can call methods as defined in the proto file. I can send a HelloRequest specifying the clients name and await the reply.

Once I have a reply I can assign the Message to the Greeting field and then close the channel to the server.

All thats left to do is fire everything up and click the button.

Summary

This has been a high-level overview of gRPC and how you can configure server-side Blazor as a client. We started with a quick introduction to what gRPC is and why we might use it, before diving into a small sample app with a gRPC service and a server-side Blazor Client.

I have only scratched the surface with gRPC and I'm looking forward to learning more about it and trying out some more advanced scenarios.