Gopher Academy Blog

Community Contributed Go Articles and Tutorials

December 01, 2017

Contributed by

Using GopherJS with gRPC-Web

Introduction

This article will talk about how to connect a GopherJS frontend to a
Go backend. If you haven’t heard about GopherJS before, it’s an open
source Go-to-JavaScript transpiler, allowing us to write Go code and
run it in the browser. I recommend taking a look at the
official GitHub repo and
Dmitri Shuralyov’s DotGo presentation
Go in the browser
for a deeper introduction.

Writing GopherJS apps is great fun and lets us avoid writing JavaScript
and all the problems associated with it. However, we’ll often want to
communicate with a backend server in order to read or write state or
issue RPC calls to other backend servers. This is generally done via a
RESTful JSON API, or maybe something like GraphQL with JSON. But using
these approaches come with several downsides:

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
usually uses 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

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.

The Improbable client is written in TypeScript, which is better than
JavaScript, but obviously not as good as Go. This is why I created the
GopherJS gRPC-Web bindings.

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 protofile
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. 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,
protoc-gen-go,
and my GopherJS protobuf plugin,
protoc-gen-gopherjs
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:
server/site.pb.go and client/site.pb.gopherjs.go.

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:

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 grpc.CallOptions
that allow per-call settings to be applied. Supported dial options can be
found on
godoc.
Note that some dial options are not entirely supported by
the bi-directional streaming method type.

The NewWebsiteClient function takes a hostname, the address of the server
to connect to, and optionally some DialOptions, that allow per-client
settings.

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:

If you want to take a look at an example of the GopherJS gRPC-Web bindings
in use, you can dive into my
grpcweb-example repo
and take a look at the demo website.

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
@johanbrandhorst or
jbrandhorst on Gophers Slack, and check out my
my blog for more stuff related to Go,
GopherJS and gRPC.