gRPC-Web
Description
It’s possible to use gRPC in a browser, considering some limitations (See state-of-grpc-web). Using it allows you to have only one interface for all your customers. It also enables server-side streams that do not exist in classic REST protocols.
All the next steps described here can be found in the dsg-example repository in the frontend/grpc-web-example directory.
We will use BUF to generate the JS API files. See Understanding differences in the grpc-web ecosystem.
It works the same as traditional gRPC, except that a proxy is charged with transforming traditional requests into enforced HTTP/2 gRPC-compatible ones.
Depending on the backend you use, you may even not need a proxy but just some middleware. But in Python, it’s still needed for now. The goal is that gRPC-web supports directly in language-specific web framework following their roadmap.
If you are considering a production deployment on a Kubernetes cluster, please read Istio Config. For local development, follow the next steps.
Understanding differences in the grpc-web ecosystem
The first step is to understand the differences between the concepts.
Protocol for gRPC in the browser
The protocol is the technical way to encapsulate gRPC request/response into compatible browser requests that are then converted by a proxy to HTTP/2 gRPC requests/response.
The existing protocols are:
gRPC-WEB: the first one created to make gRPC API work in the browser
BUF Connect: The new kid on the block that implements its own protocol that aims to include all the different protocols automatically in one. It now supports Go, Node & Browser, Swift, Kotlin. So we can’t use it with DSG for now.
The Protocol buffer compiler plugins for the browser
One of the gRPC framework core functionalities is the generation of the client code from the proto file in different languages to make the RPC work.
To compile proto files into language-specific code files, you need to use the Protocol Buffer Compiler or protoc. But as gRPC is not supporting the browser by default, it needs a plugin to work with.
- The different existing plugins are:
protoc-gen-grpc-web directly on the grpc-web release page
protobuf-javascript The protobuf open source project for compiling into js
BUF cli with:
@connectrpc/protoc-gen-connect-es The BUF plugin for generating Service
@bufbuild/protoc-gen-es The buf plugin for request and message
The Import style of the client generation
In Browser JS, the ecosystem is wild, and there are a lot of different import systems like: ESM, CommonJs, Closure. And you also have the Typescript/Javascript difference depending on your project.
Each tool has its own import style (or target) possible:
protoc-gen-grpc-web: closure, commonjs, commonjs+dts, typescript
protobuf-javascript: Closure, commonjs
@connectrpc/protoc-gen-connect-es: ESM, in js or ts
@bufbuild/protoc-gen-es: ESM, in js or ts
Note: Vite does not support commonjs as it aims to increase the speed of js compilation by using only ESM module. See.
The client
The DSG recommendation
Regarding the progress in the grpc ecosystem lately, here is what we recommend as the DSG core team:
Protocol: gRPC-web - Connect does not support python as I write these lines
Generator plugin : BUF cli - Support ESM format, the only one compatible with Vite. See Generating JS client for usage.
Import style: ESM - As we recommend BUF cli there is only ESM as import style. Choose js or ts depending on your project.
Client: Buf connect - Support gRPC-web protocol but with better message and response manipulation.
The Envoy Proxy & docker image
The default recommended proxy is Envoy. The doc of grpc-web documents how to use it and even gives you an example config file: envoy.yaml
In this example file, the important lines you need to know because you may need to change them are:
l.10: specify the listening port
l.60 & 61: specify the address and port of the grpc-server
l.26 & 48: cluster name needs to match together
To help you understand how to launch it, you can have a look in our example repository:
This can also be launched in a production environment, but if the envoy proxy is not located in the same local network it can bring latency. Please consider using Istio if in a Kubernetes deployment
Generating JS Client
By using BUF, you can upload your proto files directly to BSR and use their SDK to dynamically generate files while pushing to registry.
To better understand how it works and to provide a simple example, we will use locally generated files.
Here are the steps needed:
Install dependencies (3 in dev mode and 3 in normal mode). Example
Create the
buf.gen.yamlfile with at least the es and the connect-es plugin. Even if it can be anywhere, we recommend putting it at the root of your JS folder or your API folder. The example will only work if at the root of a Vue Vite/Webpack project as it expects an existing src folder. ExampleCopy the proto file into a proto directory created in the folder of the buf.gen.yaml file. Example
Launch the command:
npx buf generate protoExplanationA src/gen folder should be created with two files: _connect.js file with the Services/Controllers file and _pb.js with request and response message files. Example
Once these two files are generated, you are good to go to the next step.
Using JS Client
BUF has already documented this part: Using clients.
However, there are some details that can be confusing:
You need to use the
createGrpcWebTransportprotocol. ExampleIf the proto was generated by DSG, then the
_connect.jsfile exports the Service name with Controller instead of Service name. In the BUF doc,ElizaServiceshould have beenElizaController.If API fields use snake_case, they should be set and get by camelCase when using the
createGrpcWebTransportas grpc-web automatically converts fields.
Istio Config
For production deployment, you may consider the usage of Istio that produces a grpc-web proxy out of the box.
You will only need to configure the corsPolicy of your Istio VirtualService to allow requests and headers specific to gRPC-web and DSG:
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: ...
labels: ...
spec:
hosts: ...
gateways: ...
http:
- match: ...
route: ...
corsPolicy:
allowOrigin:
- "*"
allowMethods:
- POST
- GET
- OPTIONS
- PUT
- DELETE
allowHeaders:
- grpc-timeout
- content-type
- keep-alive
- user-agent
- cache-control
- content-type
- content-transfer-encoding
- custom-header-1
- x-accept-content-transfer-encoding
- x-accept-response-streaming
- x-user-agent
- x-grpc-web
- filters
- pagination
- headers
maxAge: 1728s
exposeHeaders:
- custom-header-1
- grpc-status
- grpc-message
- filters
- pagination
- headers
allowCredentials: true
Learn more about VirtualService in the Istio documentation.