GitHub - grpc/grpc-web: gRPC for Web Clients (original) (raw)

gRPC Web · npm version

A JavaScript implementation of gRPC for browser clients. For more information, including a quick start, see the gRPC-web documentation.

gRPC-web clients connect to gRPC services via a special proxy; by default, gRPC-web uses Envoy.

In the future, we expect gRPC-web to be supported in language-specific web frameworks for languages such as Python, Java, and Node. For details, see theroadmap.

Streaming Support

gRPC-web currently supports 2 RPC modes:

Client-side and Bi-directional streaming is not currently supported (see streaming roadmap).

Quick Start

Eager to get started? Try the Hello World example. From this example, you'll learn how to do the following:

Advanced Demo: Browser Echo App

You can also try to run a more advanced Echo app from the browser with a streaming example.

From the repo root directory:

$ docker-compose pull prereqs node-server envoy commonjs-client $ docker-compose up node-server envoy commonjs-client

Open a browser tab, and visit http://localhost:8081/echotest.html.

To shutdown: docker-compose down.

Runtime Library

The gRPC-web runtime library is available at npm:

Code Generator Plugins

(Prerequisite) 1. Protobuf (protoc)

If you don't already have protocinstalled, download it first from here and install it on your PATH.

If you use Homebrew (on macOS), you could run:

(Prerequisite) 2. Protobuf-javascript (protoc-gen-js)

If you don't have protoc-gen-js installed, download it from protocolbuffers/protobuf-javascript and install it on your PATH.

Or, use the third-party NPM installer:

npm install -g protoc-gen-js

3. Install gRPC-Web Code Generator

You can download the protoc-gen-grpc-web protoc plugin from ourrelease page:

Make sure all executables are discoverable from your PATH.

For example, on MacOS, you can do:

sudo mv protoc-gen-grpc-web-1.5.0-darwin-aarch64
/usr/local/bin/protoc-gen-grpc-web

chmod +x /usr/local/bin/protoc-gen-grpc-web

(Optional) 4. Verify Installations

You can optionally verify the plugins works follwoing our Hello world example:

cd net/grpc/gateway/examples/helloworld

protoc -I=. helloworld.proto
--js_out=import_style=commonjs:.
--grpc-web_out=import_style=commonjs,mode=grpcwebtext:.

After the command runs successfully, you should now see two new files generated in the current directory. By running:

Installation is successful if you see the following 2 files:

Client Configuration Options

Typically, you will run the following command to generate the proto messages and the service client stub from your .proto definitions:

protoc -I=$DIR echo.proto
--js_out=import_style=commonjs:$OUT_DIR
--grpc-web_out=import_style=commonjs,mode=grpcwebtext:$OUT_DIR

You can then use Browserify, Webpack, Closure Compiler, etc. to resolve imports at compile time.

Import Style

import_style=closure: The default generated code hasClosure goog.require()import style.

import_style=commonjs: TheCommonJS style require() is also supported.

import_style=commonjs+dts: (Experimental) In addition to above, a .d.tstypings file will also be generated for the protobuf messages and service stub.

import_style=typescript: (Experimental) The service stub will be generated in TypeScript. See TypeScript Support below for information on how to generate TypeScript files.

Note: The commonjs+dts and typescript styles are only supported by--grpc-web_out=import_style=..., not by --js_out=import_style=....

Wire Format Mode

For more information about the gRPC-web wire format, see thespecification.

mode=grpcwebtext: The default generated code sends the payload in thegrpc-web-text format.

mode=grpcweb: A binary protobuf format is also supported.

How It Works

Let's take a look at how gRPC-web works with a simple example. You can find out how to build, run and explore the example yourself inBuild and Run the Echo Example.

1. Define your service

The first step when creating any gRPC service is to define it. Like all gRPC services, gRPC-web usesprotocol buffers to define its RPC service methods and their message request and response types.

message EchoRequest { string message = 1; }

...

service EchoService { rpc Echo(EchoRequest) returns (EchoResponse);

rpc ServerStreamingEcho(ServerStreamingEchoRequest) returns (stream ServerStreamingEchoResponse); }

2. Run the server and proxy

Next you need to have a gRPC server that implements the service interface and a gateway proxy that allows the client to connect to the server. Our example builds a simple Node gRPC backend server and the Envoy proxy.

For the Echo service: see theservice implementations.

For the Envoy proxy: see theconfig yaml file.

3. Write your JS client

Once the server and gateway are up and running, you can start making gRPC calls from the browser!

Create your client:

var echoService = new proto.mypackage.EchoServiceClient( 'http://localhost:8080');

Make a unary RPC call:

var request = new proto.mypackage.EchoRequest(); request.setMessage(msg); var metadata = {'custom-header-1': 'value1'}; echoService.echo(request, metadata, function(err, response) { if (err) { console.log(err.code); console.log(err.message); } else { console.log(response.getMessage()); } });

Server-side streaming:

var stream = echoService.serverStreamingEcho(streamRequest, metadata); stream.on('data', function(response) { console.log(response.getMessage()); }); stream.on('status', function(status) { console.log(status.code); console.log(status.details); console.log(status.metadata); }); stream.on('end', function(end) { // stream end signal });

// to close the stream stream.cancel()

For an in-depth tutorial, see this page.

Setting Deadline

You can set a deadline for your RPC by setting a deadline header. The value should be a Unix timestamp, in milliseconds.

var deadline = new Date(); deadline.setSeconds(deadline.getSeconds() + 1);

client.sayHelloAfterDelay(request, {deadline: deadline.getTime().toString()}, (err, response) => { // err will be populated if the RPC exceeds the deadline ... });

TypeScript Support

The grpc-web module can now be imported as a TypeScript module. This is currently an experimental feature. Any feedback welcome!

When using the protoc-gen-grpc-web protoc plugin, mentioned above, pass in either:

Do not use import_style=typescript for --js_out, it will silently be ignored. Instead you should use --js_out=import_style=commonjs, or--js_out=import_style=commonjs,binary if you are using mode=grpcweb. The--js_out plugin will generate JavaScript code (echo_pb.js), and the-grpc-web_out plugin will generate a TypeScript definition file for it (echo_pb.d.ts). This is a temporary hack until the --js_out supports TypeScript itself.

For example, this is the command you should use to generate TypeScript code using the binary wire format

protoc -I=$DIR echo.proto
--js_out=import_style=commonjs,binary:$OUT_DIR
--grpc-web_out=import_style=typescript,mode=grpcweb:$OUT_DIR

It will generate the following files:

Using Callbacks

import * as grpcWeb from 'grpc-web'; import {EchoServiceClient} from './EchoServiceClientPb'; import {EchoRequest, EchoResponse} from './echo_pb';

const echoService = new EchoServiceClient('http://localhost:8080', null, null);

const request = new EchoRequest(); request.setMessage('Hello World!');

const call = echoService.echo(request, {'custom-header-1': 'value1'}, (err: grpcWeb.RpcError, response: EchoResponse) => { console.log(response.getMessage()); }); call.on('status', (status: grpcWeb.Status) => { // ... });

(See here full list of possible .on(...) callbacks)

(Option) Using Promises (Limited features)

NOTE: It is not possible to access the .on(...) callbacks (e.g. for metadata and status) when Promise is used.

// Create a Promise client instead const echoService = new EchoServicePromiseClient('http://localhost:8080', null, null);

... (same as above)

this.echoService.echo(request, {'custom-header-1': 'value1'}) .then((response: EchoResponse) => { console.log(Received response: ${response.getMessage()}); }).catch((err: grpcWeb.RpcError) => { console.log(Received error: <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mrow><mi>e</mi><mi>r</mi><mi>r</mi><mi mathvariant="normal">.</mi><mi>c</mi><mi>o</mi><mi>d</mi><mi>e</mi></mrow><mo separator="true">,</mo></mrow><annotation encoding="application/x-tex">{err.code}, </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">err</span><span class="mord">.</span><span class="mord mathnormal">co</span><span class="mord mathnormal">d</span><span class="mord mathnormal">e</span></span><span class="mpunct">,</span></span></span></span>{err.message}); });

For the full TypeScript example, seets-example/client.ts with the instructions to run.

Custom Interceptors

Custom interceptors can be implemented and chained, which could be useful for features like auth, retries, etc.

There are 2 types of interceptors (interfaces):

For more details, see this blog post.

Ecosystem

Proxy Interoperability

Multiple proxies support the gRPC-web protocol.

  1. The current default proxy is Envoy, which supports gRPC-web out of the box.
    $ docker-compose up -d node-server envoy commonjs-client
  2. You can also try the gRPC-web Go proxy.
    $ docker-compose up -d node-server grpcwebproxy binary-client
  3. Apache APISIX has also added grpc-web support, and more details can be found here.
  4. Nginx has a grpc-web module (doc, announcement)), and seems to work with simple configs, according to user feedback.

Server Frameworks with gRPC-Web support

Web Frameworks Compatibility