Envoy and gRPC-Web: a fresh new alternative to REST

gRPC-Web is a JavaScript client library that enables web applications to interact with backend gRPC services using Envoy instead of a custom HTTP server as an intermediary. Last week, the gRPC team announced the GA release of gRPC-Web on the CNCF blog after nearly two years of active development.

Personally, I’d been intrigued by gRPC-Web since I first read about it in a blog post on the Improbable engineering blog. I’ve always loved gRPC’s performance, scalability, and IDL-driven approach to service interaction and have longed for a way to eliminate REST from the service path if possible. I’m excited that gRPC-Web is ready for prime time because I think it opens up some extremely promising horizons in web development.

The beauty of gRPC-Web, in my view, is that it enables you to create full end-to-end gRPC service architectures, from the web client all the way down. Previously, if you wanted to use a gRPC-driven backend in conjunction with a web client you’d need to write REST API logic to translate HTTP calls to and from gRPC — work that most of us would happily avoid if we could. gRPC-Web frees your from writing yet another HTTP server by enabling you to encapsulate all of your data interfaces using Protocol Buffers (with a little help from the fabulous Envoy, which I’ll explain a bit further down).

The beauty of gRPC-Web is that it enables you to create full end-to-end gRPC service architectures, from the web client all the way down. This frees you from the need to write yet another HTTP server by enabling you to encapsulate all of your data interfaces using Protocol Buffers (with crucial help from Envoy).

The REST way

Figure 1 below shows two ways of building web applications backed by gRPC-based service architectures. In the left panel you’ll see the “traditional” REST-based way, while in the right panel you’ll see the gRPC-Web way.

Figure 1. Client-backend interaction in a REST API vs. gRPC-Web

In the left panel you’ll notice that the REST API server acts as the point of contact between the web app and the backend. In a lot of scenarios, the REST server does little more than translate HTTP calls from the client into gRPC calls to backend services.

Let’s walk through an example: a client wants to authenticate using a gRPC backend server by POSTing JSON to the HTTP server’s /auth endpoint. The HTTP server translates that POST request into an AuthRequest Protobuf message, sends that message to the backend gRPC auth server, and finally translates the AuthResponse message from the auth server into a JSON payload for the web client. As I stated in the blog post I wrote for the CNCF blog, there’s nothing wrong with this approach per se. It’s a well established pattern that plenty of developers have used quite successfully; if it works for you, keep it.

But let’s face it: it would save you a lot of work if the web clients could cut out the HTTP intermediary and just send requests directly to the backend (imagine the JavaScript auth client sending an AuthRequest message and getting an AuthResponse message back). That would mean no HTTP status codes, no JSON SerDe, and no deployment and management burden for the HTTP server itself.

In the right panel you see the new gRPC-Web alternative. You’ll notice: fewer pieces of the puzzle, one protocol throughout (yay green lines!), no HTTP logic anywhere, all data interfaces defined using .proto files. The client sends a Protobuf message to the gRPC backend and gets a Protobuf message back, period.

In order to get this benefit, there’s just one other thing you need to have in place…

The role of Envoy

Confession: I fibbed a little. Earlier I said that with gRPC-Web clients can make gRPC calls “directly” to backend services. That’s not quite true. With gRPC-Web, client calls still need to be translated into gRPC-friendly calls, but that role is now filled by Envoy, which has built-in support for gRPC-Web and serves as its default service gateway.

Figure 2 below presents a basic picture of where Envoy fits into the gRPC-Web picture. Here, a web app interacts with a backend gRPC service, which in turn relies on two other gRPC services. Envoy translates the HTTP/1.1 calls produced by the client into HTTP/2 calls that can be handled by those services (gRPC uses HTTP/2 for transport). HTTP is still going on under the hood, but neither the client nor the server need to think in HTTP terms.

The role of Envoy in a gRPC-Web application

gRPC-Web is a huge win because you don’t have to create that translation layer — you just need to provide Envoy with some basic configuration.

With gRPC-Web, client calls still need to be translated into gRPC-friendly calls, but that role is now filled by Envoy, which has built-in support for gRPC-Web and serves as its default service gateway.

Example Envoy configuration

Here’s an example YAML configuration for an Envoy proxy that listens for HTTP client connections on port 8080 and then proxies those requests to a backend gRPC service.

In general this is a pretty standard HTTP configuration for Envoy. There are just a few small differences:

  • A handful of atypical headers — x-grpc-web, grpc-status, and grpc-message — are required for handling gRPC-Web client requests (the JavaScript library automatically handles those headers).
  • The built-in envoy.grpc_web HTTP filter performs the “heavy lifting” for gRPC-Web proxying
  • The http2_protocol_options: {} specifies that the auth_service takes HTTP/2 (in this case gRPC) connections.

You just saved yourself from all of the usual rigamarole surrounding developing HTTP servers and all it took was a little YAML. No awkward mapping of HTTP verbs to API actions, no asking StackOverflow which HTTP status codes correspond to which server states, no wrangling JSON into Protobuf messages.

A new path

gRPC-Web and Envoy together provide an extremely compelling new way to do web development, one that provides the type safety of Protocol Buffers and gRPC and circumvents the many pitfalls of HTTP and REST that we know all too well. I encourage — nay implore — you to give it a try in your next project.