The State of gRPC & RSocket in the browser, JavaScript
RSocket is an exciting new advancement in the application protocol space with decent adoption and support in the Java ecosystem, mainly due to leading support in the popular Spring framework. With respect to JavaScript, RSocket hasn’t quite picked up the same level of popularity or achieved as other protocols, such as GraphQL.
gRPC 1.0 was released in August 2016 and has since grown to become one of the premier technical solutions for application communications. It has been adopted by startups, enterprise companies, and open source projects worldwide. Its support for polyglot environments, focus on performance, type safety, and developer productivity has transformed the way developers design their architectures.
So far the benefits have largely only been available to mobile app and backend developers, whilst frontend developers have had to continue to rely on JSON REST interfaces as their primary means of information exchange. However, with the release of gRPC-Web, gRPC is poised to become a valuable addition in the toolbox of frontend developers.
In this post, I’ll describe some of the history of gRPC in the browser, explore the state of the world today, and share some thoughts on the future.
Popularity and Adoption
RSocket’s Ecosystem is Undeveloped in JavaScript
In JavaScript, most developers have become accustomed to leveraging open source packages to solve a wide range of common problems, and it’s never been a better time to avoid reinventing the wheel. However, being that there is only a single reference implementation of RSocket and no well-established supporting libraries, if you expect to be able to npm install
your way to victory, you’re going to have a bad time with RSocket.
The gRPC-Web Spec

It is currently impossible to implement the HTTP/2 gRPC spec3 in the browser, as there is simply no browser API with enough fine-grained control over the requests. For example: there is no way to force the use of HTTP/2, and even if there was, raw HTTP/2 frames are inaccessible in browsers. The gRPC-Web spec starts from the point of view of the HTTP/2 spec, and then defines the differences. These notably include:
- Supporting both HTTP/1.1 and HTTP/2.
- Sending of gRPC trailers at the very end of request/response bodies as indicated by a new bit in the gRPC message header4.
- A mandatory proxy for translating between gRPC-Web requests and gRPC HTTP/2 responses.
The Two Implementations
The teams at Google and Improbable both went on to implement the spec in two different repositories5,6, and with slightly different implementations, such that neither entirely conformed to the spec, and for a long time neither was compatible with the other’s proxy7,8.
The Improbable gRPC-Web client9 is implemented in TypeScript and available on npm as @improbable-eng/grpc-web
10. There is also a Go proxy available, both as a package that can be imported into existing Go gRPC servers11, and as a standalone proxy that can be used to expose an arbitrary gRPC server to a gRPC-Web frontend12.
The Google gRPC-Web client13 is implemented in JavaScript using the Google Closure library14 base. It is available on npm as grpc-web
15. It originally shipped with a proxy implemented as an NGINX extension16, but has since doubled down on an Envoy proxy HTTP filter17, which is available in all versions since v1.4.0.
Feature Sets
The gRPC HTTP/2 implementations all support the four method types: unary, server-side, client-side, and bi-directional streaming. However, the gRPC-Web spec does not mandate any client-side or bi-directional streaming support specifically, only that it will be implemented once WHATWG Streams18 are implemented in browsers.
The Google client supports unary and server-side streaming, but only when used with the grpcwebtext
mode. Only unary requests are fully supported in the grpcweb
mode. These two modes specify different ways to encode the protobuf payload in the requests and responses.
The Improbable client supports both unary and server-side streaming, and has an implementation that automatically chooses between XHR and Fetch based on the browser capabilities.
Here’s a table that summarizes the different features supported:
For more information on this table, please see my compatibility test repo on github.
The compatibility tests may evolve into some automated test framework to enforce and document the various compatibilities in the future.
Support for RSocket in JavaScript
There exists enough library support to build something with RSocket & JavaScript, however, when it comes to running an application in production, and you start considering monitoring, telemetry, and other needs, your mileage may vary.
rsocket-js
rsocket-js is the primary reference implementation of RSocket for JavaScript. The library provides the low level base implementations for several transports, as well as a Reactive Streams implemention.
When building an application, developers will find it is much more practical to consume an abstraction library, such as rsokcet-rpc-js, rather than consume rsocket-js directly. However, library authors would likely spend a majority of their time interfacing with rsocket-js directly.
rsocket-rpc-js
rsocket-rpc-js is an abstraction library which consumes rsocket-js, and in practical terms, is likely the interface that most applications or developers would consume when working with RSocket in JavaScript.
rsocket-rpc-js — “The Standard RPC implementation for RSocket” in JavaScript.
In addition to exposing the reactive streams implementation behind service interfaces that will be familiar to traditional OOP programmers, the library also ties in nicely with RSocket message brokers that facilitate message routing, which can be core to any distributed messaging system.
Opinionated
RPC style client and server implementations are generally opinionated about the interfaces they expose and how messages are framed/formatted.
In contrast to a restful API where a consumer requests a User resource by invoking an HTTP GET request to /users/1
, where 1
is the user ID, in an RPC API it would more common to invoke a HTTP POST request to /UserService/GetUserById
and include in the request body the ID of the user { "userId": 1 }
. rsocket-rpc-js encourages these types of RPC patterns and interfaces. A method following this pattern could have a signature similar to the following: getUserById(Number: id): Single<User>
.
Tooling
Opinionated APIs and specifications empowers tooling to be built which further standardizes integrations without requiring developers to work around new ideas that present their own unique challenges. For instance, because rsocket-rpc-js and other RSocket RPC implementations follow an established spec, it is possible to generate source code for clients, servers, and message payloads in a number of languages by leveraging common tools and interchange formats such as protoc and protobuf.
An example of consuming a generated client service produced by protoc when combined with the rsocket-rpc-protobuf protoc plugin would be similar to the below:
const request = new HelloRequest();
request.setName('John Doe');
helloServiceClient.sayHello(request).subscribe({
onComplete: (response) => {
console.log(`HelloService.sayHello response received with message: ${response.getMessage()}`);
},
onError: (error) => {
console.log(`HelloService.sayHello response received with error: ${error.name}`);
console.error(error);
}
});
In the above example a service has been implemented that exposes a method that utilizes the “Request Response” flow (review the list of flows on the rsocket.io website), and this helloServiceClient
implementation can be generated by protoc when given a protobuf definition such as:
syntax = "proto3";service HelloService {
rpc SayHello (HelloRequest) returns (HelloResponse) {}
}message HelloRequest {
string name = 1;
}message HelloResponse {
string message = 1;
}
Platforms
In addition to promoting the creation of tooling, a standardized opinionated specification also supports the creation of platform services and solutions, such as netifi, which is a language agnostic broker for RSocket that aims to solve many of the common service mesh orchestration challenges.
rsocket-flowable
rsocket-flowable provides an implementation for Reactive Streams in JavaScript, and is deeply ingrained into both rsocket-js and rsocket-rpc-js. Learning the rsocket-flowable API, and what it means to work with Reactive libraries and paradigms is going to be paramount to successfully working with rsocket-js and rsocket-rpc-js.
If you are familiar with RxJS, then rsocket-flowable will likely feel familiar to you. If you are not familiar with RxJS, or reactive programming paradigms, then you could experience a somewhat steep learning curve.
Special thanks:
Special thanks to highly experienced Lead Engineer, Team Lead, Delivery Manager Sergei Mikheev for valuable contribution.
Sources:
The State of RSocket in JavaScript, https://grpc.io/blog/state-of-grpc-web/
The state of gRPC in the browser, https://viglucci.io/the-state-of-rsocket-in-javascript
How to bring a gRPC defined API to the web browser, https://stackoverflow.com/questions/35065875/how-to-bring-a-grpc-defined-api-to-the-web-browser