First Steps in gRPC Bindings for React Native

When you want to use gRPC in your React Native app there is no official support yet, but that shouldn’t stop you! In this post I’ll show you how we designed an implementation with type safety in mind and successfully called a service remotely from React Native on Android.

The Rationale

At the previous Innovation Day of Xebia, I had the pleasure of teaming up on implementing a microservice architecture using gRPC. gRPC is a remote procedure call framework (as the name suggests) and focuses on both high performance as well as availability in the most common programming languages. It utilises protocol buffers, a binary format for communicating messages, which is normally passed over the HTTP2 stack.

My personal goal was to create an Android React Native binding for one of the ‘consuming’ client services in our architecture. One possible solution we looked into was to use the protoc compiler that comes with the gRPC framework to generate JavaScript service bindings for Node and try to get these to work in the React Native JavaScript engine. However, a long standing issue shows that the current Node client implementation will not work in React Native without some serious tweaking, so we dropped that idea quite soon.

The Plan

After a short sketching session, we came to the conclusion that we could leverage the React Native bridge and use the generated Android Java RPC stub services without many painful memory translations. Furthermore we scoped the solution by only implementing a single request/response. Streaming responses will be a topic for another Innovation Day and post.

Our design of a single gRPC request from React Native to a gRPC endpoint looks like this:

From the JavaScriptCore environment, a NativeModule API is called with a JavaScript object as its request payload and returns a Promise. React Native stores the JavaScript object in shared memory and calls the exposed native Android method. This method receives a ReadableMap instance that has access to the JavaScript object properties, and a Java Promise instance to respond asynchronously.

Then, the native method creates a RPC Request object from the ReadableMap data and performs the actual request using the generated service stub. This transforms the request payload into a protocol buffer byte array which is sent over to the server.

After the server responds to the request, the happy path response handling looks like this:

The generated service stub is provided with a callback method which transforms the server response into a WritableMap object and resolves the JavaPromise with that object. This triggers a resolve at the JavaScript side which receives the JavaScript object that contains the response data.

A nice bonus is that we can reject the provided promise if anything goes wrong during the transform, network call etc. In JavaScript, we can easily try .. catch such an exception.

The Implementation

We start simple by basing our application on one of the classic examples from gRPC: a hello world Android client and Java server. The shared RPC interface contract is defined in a .proto file as follows:

Based on this definition, a GreeterGrpc class can be generated for the Android client which handles the actual network interaction, together with HelloRequest and HellyReply classes which handle the (de)serialization of requests and responses.

In a freshly installed React Native project, we add the com.google.protobuf plugin and dependencies to the app Gradle configuration and set it up just like the official Android example. On every recompile, the React Native project now gets freshly generated stubs and message classes.

Now we need to use those generated classes. We create a React Native module that exposes a method which makes the underlying stub call available in JavaScript:

The ResponseTask (which is omitted for brevity) takes care of the gRPC channel and does the network call on a separate thread. If anything goes wrong on this thread, the responsePromise is rejected with the thrown Exception.

Now all we need to do is access the native module from JavaScript. To make things more robust on the JavaScript side, we add some flow types in the mix. With type checking, we can ensure that we are reading and writing the right properties in the JavaScript request/response objects. Also, a good IDE can hint us how our API looks like.

Here’s what our implementation looks like:

React Native exposes all native modules via the NativeModules module. Because the sayHello native method accepts a Java Promise argument, the NativeModules JavaScript call is returning a JavaScript Promise. We just wrap this function with some extra type information and export it in a nested fashion.

To use it, we can import Greeter and await the result of sayHello:

And this works perfectly, passing and transforming the message from JavaScriptCore to native Android to the Java example server and back!

The Work in Progress

After this successful roundtrip, we added a nice and shiny React Native UI on top (as you already spotted in the top image). I started to work on a tool to generate the Java module and JavaScript wrapper with flow types, because friends don’t let friends write code that could have been generated. This tool uses the same .proto file as input and can be run at build time together with the generation of the stubs. However, this tool is by far from finished and not production-ready, as supporting all features from gRPC is a little too much for a single day.

Conclusion

All in all it was an exciting Innovation Day and good to see that we have a working gRPC solution for React Native on Android in less than a day.

The example project code is available at GitHub.