Article Using gRPC-Web with .Net Core and Angular

The REST API has been the standard to communicate between clients and servers for a lot of years now. But recently gRPC has gained a lot of popularity on this territory.

Two reasons for this popularity are:

  1. gRPC offers better performance and security. It heavily promotes the use of SSL/TLS to authenticate the server and to encrypt all the data exchanged between the client and the server.
  2. It is based on Protocol Buffers, an open source mechanism for serializing structured data, which is language and platform neutral. Similar to XML, Protocol Buffers are verbose and descriptive. But they are smaller, faster, and more efficient than other wire-format protocols.

Although gRPC has excellent cross-platform support, there is one place you can’t call a gRPC service from and that is... the browser. gRPC heavily uses HTTP/2 features and no browser provides the level of control required over web requests to support a gRPC client. For example, browsers do not allow a caller to require that HTTP/2 be used or provide access to underlying HTTP/2 frames.

gRPC-Web

gRPC-Web is an additional technology from the gRPC team that provides (limited) gRPC support in the browser. gRPC-Web consists of two parts: a JavaScript client that supports all modern browsers, and a gRPC-Web proxy on the server. The gRPC-Web client calls the proxy and the proxy will forward on the gRPC requests to the gRPC server.

The need for this proxy made it a little bit harder to use, but Microsoft announced experimental support for gRPC-Web with .NET! So now we don't need this extra proxy anymore.

In this blogpost I will demonstrate a working example of a .NET Core service communicating with an Angular frontend using gRPC-Web (without the use of an Envoy proxy).

Create the gRPC service

For the service I have used the default "gRPC Service" project provided by Visual Studio.

Start Visual Studio and select Create a new project.

In the Create a new project dialog, select gRPC Service and select Next:

Create new project

After creating this project, you will have an GreeterService which can communicate by gRPC.

Add the Grpc.AspNetCore.Web NuGet package to enable gRPC-Web (don't forget to check the "Include prerelease" box) Add Nuget

And configure the app to use gRPC-Web by adding the UseGrpcWeb() middleware and .EnableGrpcWeb() to your service in the startup file:

Startup.cs

public void Configure(IApplicationBuilder app)
{
    app.UseRouting();

    // Add gRPC-Web middleware after routing and before endpoints
    app.UseGrpcWeb();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapGrpcService<GreeterService>().EnableGrpcWeb();
    });
}

Call the gRPC-Web service from an Angular app

Now that we created the server part, we need to create a client to communicate with the server. For the demo we create a new Angular app by using the ng-cli command:

ng new GrpcWebClient

Because the communication between client and server is defined in a .proto file we can generate the client code for the service.

For this to work we need to add 2 packages to our Angular project

npm install ts-protoc-gen
npm install google-protobuf @types/google-protobuf @improbable-eng/grpc-web --save

And we need to download the protoc tool

I have downloaded the "protoc-3.11.4-win64.zip" and copied the protoc.exe and google folder to a new "Protoc" folder (next to my GrpcWebClient folder)

Content of Protoc folder

To generate the client code I copied the greet.proto from the .NET project to the Protoc folder and used this command

protoc 
  --plugin="protoc-gen-ts=C:\Repos\GrpcWeb-dotNetCore-Angular\GrpcWebClient\node_modules\.bin\protoc-gen-ts.cmd" 
  --js_out="import_style=commonjs,binary:../GrpcWebClient/src/generated" 
  --ts_out="service=grpc-web:../GrpcWebClient/src/generated" greet.proto

After running this command, it will generate 4 files in my '/GrpcWebClient/src/generated' folder (the 'generated' folder needs to be created by hand).

Now we can add a greeter component by using this command

ng g component greeter

Call the Greeter service from Angular

greater.compontent.ts

export class GreeterComponent implements OnInit {
  response: string;

  ngOnInit(): void {
    const client = new GreeterClient('https://localhost:5001');
    const req = new HelloRequest();
    req.setName("World!");
    client.sayHello(req, (err: ServiceError, response: HelloReply) => {
      if (err) {
        this.response = `Error: {err.message}`;
        return;
      }
      this.response = response.getMessage();
    });
  }
}

After running the client (and server) you will have the "Hello World!" example of a browser to server application using gRPC-Web for communication. For more information on gRPC see grpc.io