Skip to content

Full feature integration library for gRPC-Web into React

Notifications You must be signed in to change notification settings

oslabs-beta/ReactRPC

Repository files navigation

ReactRPC

badge badge badge ​ Full featured integration library for React and gRPC-Web. Core functions include: packaging the generated proto messages and client stubs, a unified API of gRPC call methods that support Google's and Improbable's gRPC-web specs for unary, client streaming, server streaming and bi-directional streaming. ​

Getting Started

Install

npm install --save reactrpc

1. Define the Services

Create proto files as the schema for your Server and Client Stubs. It should define the gRPC call methods needed to communicate between the Server and Browser. These files will be used to give your components superpowers -- Remote Procedure Call (RPC) methods. ​ helloworld.proto

syntax = "proto3";
​
package helloworld;
​
service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply);
}
​
message HelloRequest {
  string name = 1;
}
​
message HelloReply {
  string message = 1;
}

book_service.proto

syntax = "proto3";
​
package examplecom.library;
​
message Book {
  int64 isbn = 1;
  string title = 2;
  string author = 3;
}
​
message GetBookRequest {
  int64 isbn = 1;
}
​
message QueryBooksRequest {
  string author_prefix = 1;
}
​
service BookService {
  rpc GetBook(GetBookRequest) returns (Book) {}
  rpc QueryBooks(QueryBooksRequest) returns (stream Book) {}
}
​

2. Generate a Protobuf Messages and Client Service Stub

​ In order to pass superpowers to our Browser, we first need to package our .proto file. ​

For Google's implementation:

To generate the protobuf messages and client service stub class from your .proto definitions, we need the protoc binary and the protoc-gen-grpc-web plugin. ​ You can download the protoc-gen-grpc-web protoc plugin from Google's release page: ​ If you don't already have protoc installed, you will have to download it first from here. ​ Make sure they are both executable and are discoverable from your PATH. ​ For example, in MacOS, you can do: ​

$ sudo mv ~/Downloads/protoc-gen-grpc-web-1.0.7-darwin-x86_64 \
  /usr/local/bin/protoc-gen-grpc-web
$ chmod +x /usr/local/bin/protoc-gen-grpc-web

​ When you have both protoc and protoc-gen-grpc-web installed, you can now run this command: ​

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

​ After the command runs successfully on your [name of proto].proto you should see two generated files [name of proto]_pb.js which contains the messages and [name of proto]_grpc_web_pb.js that contains the services: ​ For instance the helloworld.proto file will generate to:

  • messages : helloworld_pb.js
  • services : helloworld_grpc_web_pb.js

For Improbable's implementation:

​ For the latest stable version of the ts-protoc-gen plugin: ​

npm install ts-protoc-gen

​ Download or install protoc (the protocol buffer compiler) for your platform from the github releases page or via a package manager (ie: brew, apt). ​ Download protoc from here ​ When you have both protoc and ts-protoc-gen installed, you can now run this command: ​

--plugin=protoc-gen-ts=./node_modules/.bin/protoc-gen-ts \
  -I ./proto \
  --js_out=import_style=commonjs,binary:./ts/_proto \
  --ts_out=service=true:./ts/_proto \
  ./proto/examplecom/library/book_service.proto

After the command runs successfully on your [insert_name].proto you should see two generated files [insert_name]_pb.js which contains the messages and [insert_name]_pb_service.js that contains the services: ​ For instance for the helloworld.proto you should see:

  • messages : book_service_pb.js
  • services : book_service_pb_service.js ​ ​

3. Create proxy server

​ In order for gRPC-web to communicate with other gRPC servers, it requires a proxy server as a translation layer to convert between gRPC-web protobuffers and gRPC protobuffers. Links to examples on how to set those up can be found here (Envoy proxy) and here (Improbable's proxy)* ​

*Note: To enable bidirectional/client-side streaming you must use Improbable's spec and its proxy with websockets enabled ​

4. Set up React component

​ Require in the reactRPC library and protobuf files in your React JSX file. Run the build method with the following params: the message, the services and the URL to the proxy server endpoint. ​

Google's Implementation

const { googleRPC } = require("reactRPC")const messages = require("helloworld_pb.js")const services = require("helloworld_grpc_web_pb.js")const URL = "http://" + window.location.hostname + ":8080"googleRPC.build(messages, services, URL)

Export the googleRPC component by passing it as an argument into the reactRPC wrapper as follows: ​

export default googleRPC.wrapper(<your component>);

Improbable's Implementation

const { improbRPC } = require("reactRPC")const messages = require("book_service_pb.js")const services = require("book_service_pb_service.js")const URL = "http://" + window.location.hostname + ":8080"improbRPC.build(messages, services, URL)

​ Export the improbRPC component by passing it as an argument into the improbRPC wrapper as follows: ​

export default improbRPC.wrapper(<your component>);

5. Define a message

​ We define a request message by creating an object with the keys as the message field along with a msgType property specifying a message that we set in the proto file. Here is an example of a HelloRequest message in the helloworld.proto file : ​ ​

const message = { name: "John", lastName: "Doe", msgType: "HelloRequest" }

6. Create the function

​ We define a function by listing its service and procedure calls on this.props. We then pass in the message we defined above, and an object with any metadata data required (learn more about metadata here). For unary calls a third parameter of a callback is required while streaming calls have built in event listeners. ​ ​

// unary call:this.props.Greeter.sayHello(
  message,
      {},
      (err, response) => {
        console.log(response)
      }
    );// streaming callconst stream = this.props.Greeter.sayRepeatHello(
  message,
      {}
    );
    stream.onMessage(res => {
      console.log(res.getMessage());
    });

ReactRPC library supports unary, client-side, server-side and bi-directional streaming.
​ ​

Additional details

Nested Messages

​ In the proto file, messages can be nested in other messages. In this example, the FullName message is used in the TestNested message: ​

message FullName{
  string name = 1;
  string lastName = 2;
}
​
message TestNested{
  FullName myName = 1;
}

Event listeners for Streams

​ For flexibility ReactRPC models both Google and Improbable's eventlisteners: ​

Google's Implementation

const stream = this.props.Greeter.sayRepeatHello(
  message,
      {}
    );
    stream.on("data", res => {
      console.log(res.getMessage());
    });
    stream.on("status", res => {
      console.log(res.getMessage());
    });
    stream.on("end", res => {
      console.log(res.getMessage());
    });

Improbable's Implementation

const stream = this.props.Greeter.sayRepeatHello(
  message,
      {}
    );
    stream.onMessage(res => {
      console.log(res.getMessage());
    });
    stream.onHeaders(res => {
      console.log(res.getMessage());
    });
    stream.onEnd(res => {
      console.log(res.getMessage());
    });

For improbable's bidirectional streaming there is client.send that opens the stream and client.finishSend to close the stream as follows: ​

const stream = this.props.Greeter.sayRepeatHello(
  message,
      {}
    );
    stream.send({ name: "John", lastName: "Doe", msgType: "SayHelloRequest" });
    });
    stream.finishSend({ name: "John", lastName: "Doe", msgType: "SayHelloRequest" });
    });

About

Full feature integration library for gRPC-Web into React

Resources

Code of conduct

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published