Building Microapps with gRPC-Web

gRPC for Web Clients might boost your organization’s efficiency in an unprecedented way. Here’s how with an implemented flow! (Go, React)

Oggy
6 min readFeb 11, 2022

Imagine every team in an organization has the ability to do end-to-end development in their domain.

Turning “horizontal teams” (teams that are only responsible from the Frontend or the Backend) to “vertical teams” (teams that are performing full-stack development on a portion of the system). Doesn’t that sounds highly beneficial in many aspects?! Of course, it’s easier said than done.

Horizontal Teams vs Vertical Teams

There should be guidelines and tools not only to help teams in developing those blocks but also to maintain coherence over the entire system, especially when it comes down to integration. In such scenarios the importance of standardization is unquestionable. Apart from major improvements in performance, security, and extensibility, code generation by the protocol buffer compiler makes gRPC a perfect candidate.

Let’s discover how exactly gRPC-Web could help, and go through the steps to make it happen. You will also find a flow (full-stack implementation of Go services with their dedicated React.js pages) as shown below. This flow is used to demonstrate available service method types, in other words, a perfect playground.

The Flow

Utilized service method types and the functionality provided by each step of the flow could be summarized as such;

  1. Pick a catalog item (Unary)
  2. Select a real-time generated offer (Server-side Streaming)
  3. Track the order in real-time and rate (Server-side Streaming & Unary)

TL;DR: Demo, GitHub

Each step of the flow is designed to represent a “vertical team” as described earlier. Having this mindset, the high-level diagram of the flow is as follows;

High-Level Diagram

There are lots of practical examples which demonstrate how to establish communication between microservices using gRPC. However, in this story, we will be taking a step further by utilizing gRPC-Web for fast and consistent inner-block communication, e.g. in between a “Vertical Team” UI and microservice.

It’s totally normal to be overwhelmed or agitated after seeing all the things we are supposed to do to set up such flow. Fear not! You will be truly surprised when realizing how easy it is. The main steps for allowing parties to establish integration using gRPC-Web are;

  1. Create a .proto file
  2. Generate code with protoc using our .proto file
  3. Configure envoy proxy .yaml

Let’s start with creating a basic .proto file.

.proto (IDL) is used to define everything we need in our gRPC service. We add all of the required message, service , rpc , enum types here. They will be used by the protoc during the code generation phase.

Once we are done with the .proto file, we can continue with the code generation phase for the service and the client.

protoc & plugins ❤

For the service, we will need two files to be generated by the protoc

Running the command;

protoc --go_out=. --go-grpc_out=. catalog.proto

will generate the aforementioned files in the current directory.

The same applies to the client but this time we will get help from the gRPC-Web plugin

Running the command;

protoc --js_out=import_style=commonjs,binary:. --grpc-web_out=import_style=commonjs,mode=grpcwebtext:. catalog.proto

will generate the files mentioned above. Notice gRPC-Web plugin offers additional client configuration options such as:

Finally, we are required to provide a .yaml configuration for the envoy proxy.

Shooting the messenger

gRPC-web clients connect to gRPC services via a special proxy; by default, gRPC-web uses Envoy. Source: grpc/grpc-web

Please visit this link to review a sample configuration. We are adding filters, gRPC-Web related headers, and updating endpoint addresses based on our environment. Building an Envoy Proxy container image with custom configuration is straightforward. Below, there is a sample .Dockerfile which is also used in this demo.

By executing the following command (as in the same directory with the .Dockerfile ) we should have our custom Envoy Proxy container image ready with a name & tag.

docker build . -t <name>:<tag>

That’s it! Fast and consistent integration in just 3 steps. Note that for any update we only need to reflect it on .proto file and re-run the code generation. Another great benefit here is that since all messages and properties inside them are known after the code generation, lots of bugs (either on request or response) that could occur during execution are already prevented!

GetItems request over Envoy

As depicted in the “Shooting the messenger” figure, GetItems flow is initiated by the Web Client, passed on to the Envoy, and then reached to the service, vice versa for the response.

If you are interested in how to implement a gRPC Service please check the previous story. The same structure applies to the services in this demo as well.

Previous Story / gRPC Service In-depth

Now that everything is ready, let’s take a closer look at gRPC examples at UI.

  • catalogpb.Catalog.GetItems (Unary)
GetItems / gRPC @ UI

In the above figure, we are importing a message (GetItemsRequest) and client (CatalogClient) from the generated files. Later on, we are preparing our client by giving the URL address of the associated Envoy Proxy and we are good to go! In order to make Unary RPCs, three parameters should be provided.

client.unary(request: Function, metadata: Object, callback: Function)

Once the request is completed, the callback will be executed. Based on the result one can update the UI. In this example, we are simply showing an error notification if the gRPC service returns an error. Otherwise, we are converting the response to an object and getting the itemList property that was defined earlier in the .proto message. An example of what a received gRPC error looks like in the Web Client is shown below.

Sample Error
  • offerpb.Offer.GetOfferStream (Server-side Streaming)
GetOfferStream / gRPC @ UI

Similar to the Unary RPC example, we need to import a message (GetOfferStreamRequest) and client (OfferClient) from the generated files. Once the client is prepared, we can perform Server-side streaming RPCs.

This time we are setting some properties in the request (business logic) before performing a call. Once the client.getOfferStream() is executed it does return us a ClientReadableStream<GetOfferStreamResponse>.

By listening to events such as data , end , error on that readable stream, we are able to receive information and update the UI based on different events. Finally, we are unsubscribing from the stream in the clean-up to prevent any memory leaks.

Still reading? You’re amazing 🤩. Thank you very much for your attention and if you are interested in working with awesome people check us at:

We are growing super fast. Feel free to fork repositories and start hacking around, everything you need is included. Services, UI.

Probably we will hear much more about gRPC for Web Clients when companies start to move towards Microfrontends. Who can say no to built-in speed, performance, security, and robustness, right?

Also, If your organization has “vertical teams”, it’s likely that they will be able to come up with unique solutions rather than dull repetitions which sounds like an amazing environment for collaboration and growth.

--

--