Using GopherJS with gRPC-Web
- Having two sources of truth for the interface
- Loosely typed JSON objects
- Complex API versioning
- No streaming support
- JSON Marshalling/Unmarshalling is slow
Fortunately there’s now a great alternative to REST and GraphQL which
builds on the existing gRPC ecosystem. gRPC, of
course, is the open source RPC framework developed by Google, donated
to the CNCF, and generally accepted as one of
the best ways to faciliate RPCs between microservices today. It
protobuf as the payload, but is designed to be agnostic
of the payload layer.
Protobuf, of course,
is a payload format, also developed by Google, used for fast and
efficient data transfers.
gRPC-Web is essentially a gRPC client in the browser. It allows the use of normal gRPC-like requests over the wire, with binary marshalled protobuf messages as the payload. It currently requires a small proxy to be compatible with existing gRPC backends, but this requirement will eventually be dropped, as it becomes possible to implement the gRPC wire protocol in the browser.
It has an official spec, but currently no official client. Google was working on a client in a private repo, which was scheduled for a Q3 2017 release, but work has mostly died down for now. Instead, there’s a spec-compliant third party client available, developed by Improbable. Improbable wrote a great blog post to introduce the library, which I encourage you to read.
gRPC-Web supports unary and server-side streaming methods only at this time, pending the finalizing and implementing of the WHATWG Streams API in browsers.
GopherJS gRPC-Web bindings
The GopherJS gRPC-Web bindings allows the use of gRPC as easily as any other Go gRPC client, and with a suitably proxied gRPC backend it can act as the communications layer between the frontend and backend of a website. It supports all 4 streaming modes supported by gRPC, bridging the client-side streaming gap in the gRPC-Web spec with the use of WebSockets.
Working with protobuf usually consists of 3 steps, and with my bindings, it is no different:
- Define the interface
- Generate the code
- Use the generated code
I’ll now give a quick example of how to use the bindings, which should look extremely familiar if you’ve already used protobuf and gRPC. If you want to, you can following along these instructions after cloning my boilerplate repo.
Define the interface
We’ll define a simple unary server method for getting information about
a user, with the user ID as the input. We’ll also add a fancy
bi-directional streaming method. The following
is all you need to generate the server and client interfaces to perform
these RPC calls:
Generate the code
We generate the server and client interfaces using
protoc can be
daunting, but I won’t dedicate time to it in this post, if you want
more of an introduction, I recommend reading
my blog post on the
subject. The following command will generate both the server and client
code and interfaces:
We use the standard Go protobuf plugin,
and my GopherJS protobuf plugin,
to generate the server and client respectively. It is important that
the generated files are put into different folders as they expect to
define their own packages. This will generate two files:
Use the generated code
The code generated by
protoc-gen-go defines an interface that the
backend must implement. In our case, it looks like this:
This is all standard gRPC stuff, so I wont talk too much about this.
The code generated by
protoc-gen-gopherjs instead exposes a client
that, when pointed at a gRPC server, allows calling to the functions
implemented by the backend. The functions exposed intentionally
mirror that of the client generated by
protoc-gen-go, in order
to make it more familiar. Lets take a look:
The generated functions take a context, that can be used for cancellation,
just like in a normal gRPC request. It optionally takes
that allow per-call settings to be applied. Supported dial options can be
Note that some dial options are not entirely supported by
the bi-directional streaming method type.
NewWebsiteClient function takes a hostname, the address of the server
to connect to, and optionally some
DialOptions, that allow per-client
All the functions exposed by the GopherJS gRPC-Web bindings block until their respective calls have completed.
Server side requirements
As mentioned earlier, the GopherJS gRPC-Web bindings (for now) require a small proxy in front of a generic gRPC server. The proxy is developed by Improbable, and there are two different packages. Since we’re working with a Go gRPC backend, we’ll use the importable package which makes this extremely simple. This is all that is required to proxy gRPC-Web requests into gRPC requests in your backend:
It’s as simple as wrapping the
In addition, if you want to use the bi-directional streaming capabilities of the GopherJS bindings, you’ll need to wrap the server again.
This second wrapper intercepts websocket requests made to the server and translates them into gRPC streaming requests to support client side and bi-directional streaming.
The GopherJS gRPC-Web bindings solve most of the problems associated with a classic RESTful JSON API:
- One source of truth for the interface design - the
- Strongly typed data structures via protobuf and Go.
- Simple API versioning - protobuf is backwards compatible by design.
- Built-in streaming support; server-side, client-side and bi-directional.
- Fast and efficient marshalling and unmarshalling via protobuf.
If you want to try it out for yourself, I would encourage you to clone the boilerplate repo I set up to get going quickly.
I hope this post has inspired you to try something new next time you’re
writing a webserver with a frontend client. If you have any questions
or comments, please reach out to me
jbrandhorst on Gophers Slack, and check out my
my blog for more stuff related to Go,
GopherJS and gRPC.