How To Write Modern React App Using gRPC And Envoy
By: Sergey Suslov
Tags: JavaScript, Envoy Proxy, Microservices, Nestjs, Grpc

If you lose a bunch of time every time, adjusting your client app to the backend REST API, this article is here to save you.

What is wrong?

One of the big problems with REST API is a specification. If you develop a frontend app in React/Angular/Vue, most of the time you use an API. And most of the time, this API is written by another developer.

The specification for this API must be elaborated in advance, in order to develop client-side and server-side in parallel. Elaborated specification is hard to modify. Because you have to notify all other developers about changes, not to mention the probability of mistypes in an implementation.

Here are two main problems:

Complexity of modification

The probability of mistypes in an implementation

Our savior gRPC

GRPC is an open-source universal RPC framework. It is used to build communications between services in a microservice architecture. This technology uses Protobuf as a serializer.

GRPC has many various advantages and features. We, as frontend developers, care only about these two for now:

Code generation

Proto files

Code generation

This feature extinguishes the probability of mistypes on the implementation stage. There are tools to generate concrete methods and interfaces in multiple programming languages. This feature makes possible implementation of business logic 100% accurate according to a proto specification.

Proto files

Proto file is a single source of truth for an API. They are easy to modify, and can be stored near to project files in order to keep a version history.

.proto file example

Building an Example

Below, we will try to build a frontend app using gRPC. This pretty simple app will be able to sum two numbers. The server from the previous article will be used as a backend to provide an API for our client-side application.

Server code can be found here

Interface

Let’s start with UI. And build a very simple interface with two input fields for number A and number B. Also, we have to add a button to trigger API calls and a result field.

It will look like this:

Here is the code:

Code generation

We built the UI part of the application. It’s time to get to work on code generation. We will use the .proto file of the backend application. It can be found here.

We will use GatewayService and its method Add, to sum the numbers A and B.

To generate some code from this .proto you should have protoc installed on your computer. We will use grpc-web plugin in order to generate typescript code for usage in a browser.

Generated files may not meet the rules of your eslint configuration. In this case, you can insert /* eslint-disable */ at the beginning of every generated file. I wrote a script for typescript code generation with insertion of the rule /* eslint-disable */ :

This script generates files using protoc. The .proto file is located at src/proto/sum.proto .

API calling

From now, when all required files are generated and the UI is ready, we can start API calling implementation.

Here is the code:

On line 12 I create gatewayServiceClient, from the generated sum_pb.ts , to call the Add method of the GatewayService.

On line 15 I create AddRequest, then set A and B numbers.

Unary call methods are created in callback style. In order to work with the API in promise style, I create sumNumbers function using promisify.

On line 19 I perform an API call to the GatewayService and get the result object.

API call integration into the UI

This API calling style is an anti-pattern. But for educational purposes, I thought it would be acceptable.

I extended the App component with the API call:

On line 7 I create a callback that is used as an onClick handler for the button Sum. The handler takes the value of A and B input fields and calls sum. When the call ended, it sets the result.

Envoy

Unfortunately, the browser API is not enough to fully implement gRPC on the client-side. To get around this restriction Envoy server can be used.

Envoy can be used as a proxy server. And it has built-in support for transforming web gGRPC to the server-friendly gRPC.

I use docker-compose to set up a local environment. Here is my setup for Envoy container:

So you have to create docker-compose.yml with config directory. Then put Dockerfile-gateway and gateway-envoy.yaml files into the config directory.

Here is docker-compose.yml :

That is Dockerfile-gateway :

And, finally, gateway-envoy.yaml :

This Envoy configuration contains envoy.grpc_web HTTP filter, that transforms gRPC-web into server-friendly gRPC.

Now you can run this Envoy configuration with this command:

docker-compose up — build -d

When the Envoy server is running, it will proxy every response from the port 8080 (the client app should send in here) to the 50051 (the server app should listen to this port) port.

Demonstration

When the Envoy server is running, you can start the backend services and the frontend application. Enter two numbers into the fields and press Sum:

The result will look like this: