@grpc/grpc-js support
April 23, 2020 ยท View on GitHub
Why
@grpc/grpc-js is a great project. it's pure js implementation, this makes it's easy to build docker images & cooperation with electron, etc.
Recently grpc-js has published it's 1.0.1 version, means it's no longer beta. So it's a good time to support it.
Some information
- grpc: github | npm
- grpc-js: github | npm
- [question] How to generate
require('@grpc/grpc-js')instead ofrequire('grpc')#931 - Add support for generate_package_definition #56
Migrating from grpc to @grpc/grpc-js
Code generation
Want to use grpc-js, grpc-tools version 1.8.1 is REQUIRED.
Change your bash script from:
grpc_tools_node_protoc \
--js_out=import_style=commonjs,binary:./src/grpc/proto \
--grpc_out=./src/grpc/proto \
--plugin=protoc-gen-grpc=`which grpc_tools_node_protoc_plugin` \
-I ./proto \
proto/*.proto
to:
grpc_tools_node_protoc \
--js_out=import_style=commonjs,binary:./src/grpcjs/proto \
--grpc_out=generate_package_definition:./src/grpcjs/proto \
-I ./proto \
proto/*.proto
--plugin is no longer necessary and add generate_package_definition in --grpc-out.
There are two generated files:
*_pb.js: Message codes have NO changes*_grpc_pb.js: Service codes have some changes
- var grpc = require('grpc');
- var BookServiceService = exports.BookServiceService = {
+ var BookServiceService = exports['com.book.BookService'] = {
- exports.BookServiceClient = grpc.makeGenericClientConstructor(BookServiceService);
grpcis no longer necessary- exported service object has new name, a package name like:
com.book.BookService - client object is no longer generated, users have to make it by self
Typings
grpc has it's official typings: index.d.ts. Also grpc-js has an official typings itself (it's not included in github's online source codes), it's in the dir node_modules/@grpc/grpc-js/build/src/index.d.ts, and online source codes may give some hint: index.ts.
There are several differences between them:
All the calls in grpc-js has both request & response definition: grpc.ServerUnaryCall<GetBookRequest, Book> while grpc only needs the request part: grpc.ServerUnaryCall<GetBookRequest>. This is the most big change.
And in version 1.0.1 grpc-js has two useful typings not exported in typings index, you need to manually import them:
import {sendUnaryData} from "@grpc/grpc-js/build/src/server-call";
import {handleClientStreamingCall} from "@grpc/grpc-js/build/src/server-call";
I guess it shall be a bug, maybe would be fixed later.
Usage
See full examples here:
Most changes:
server.ts
add services part:
// grpc
import { BookServiceService, ... } from "./proto/book_grpc_pb";
server.addService(BookServiceService, new ServerImpl());
// =>
// grpc-js
import * as bookGrpcPb from "./proto/book_grpc_pb";
// @ts-ignore
server.addService(bookGrpcPb["com.book.BookService"], new ServerImpl());
Since service object in js file is now exported with name like: "com.book.BookService", it's no longer possible to be imported with sentence: import { YourService } from "your_grpc_pb".
And // @ts-ignore is required, without this there would be compiling error: Index signature is missing in type 'ServerImpl'.. Some information could be found here: Index signature is missing in type (only on interfaces, not on type alias) #15300. Though it may be possible to be fixed by some hack, I just use comments to ignore it, since the implementation is correct.
start server part, changed to async:
// grpc
server.bind("127.0.0.1:50051", grpc.ServerCredentials.createInsecure());
server.start();
// =>
// grpc-js
server.bindAsync("127.0.0.1:50051", grpc.ServerCredentials.createInsecure(), (err, port) => {
if (err) {
throw err;
}
server.start();
});
client.ts
client object changes:
// grpc
import { BookServiceClient } from "./proto/book_grpc_pb";
const client = new BookServiceClient("127.0.0.1:50051", grpc.credentials.createInsecure());
// =>
// grpc-js
import * as bookGrpcPb from "./proto/book_grpc_pb";
const BookServiceClient = grpc.makeClientConstructor(bookGrpcPb["com.book.BookService"], "BookService");
const client = new BookServiceClient("127.0.0.1:50051", grpc.credentials.createInsecure());
Since client object is no longer created by grpc-tools code generation, it have to be generated by user codes.
What grpc_tools_node_protoc_ts changed
d.ts generating bash script change, from:
grpc_tools_node_protoc \
--plugin=protoc-gen-ts=../bin/protoc-gen-ts \
--ts_out=./src/grpc/proto \
-I ./proto \
proto/*.proto
to:
grpc_tools_node_protoc \
--plugin=protoc-gen-ts=../bin/protoc-gen-ts \
--ts_out=generate_package_definition:./src/grpcjs/proto \
-I ./proto \
proto/*.proto
Add generate_package_definition in --ts-out, be consistent with grpc-js.
There is no changes in *_pb.d.ts, and some changes in *_grpc_pb.d.ts:
- import * as grpc from "grpc";
+ import * as grpc from "@grpc/grpc-js";
+ import {handleClientStreamingCall} from "@grpc/grpc-js/build/src/server-call";
export interface IBookServiceServer {
getBook: grpc.handleUnaryCall<book_pb.GetBookRequest, book_pb.Book>;
getBooksViaAuthor: grpc.handleServerStreamingCall<book_pb.GetBookViaAuthor, book_pb.Book>;
- getGreatestBook: grpc.handleClientStreamingCall<book_pb.GetBookRequest, book_pb.Book>;
+ getGreatestBook: handleClientStreamingCall<book_pb.GetBookRequest, book_pb.Book>;
getBooks: grpc.handleBidiStreamingCall<book_pb.GetBookRequest, book_pb.Book>;
}
That's all.
If you are still using grpc, just do nothing, new version would not affect existing users.