参考
こちらの素晴らしい記事を全面的に参考にしていきます。
この記事でする事
- 参考記事の通りに進めると、Windows と lint 由来の躓きポイントやがあります。これに対応していきます。
- コマンドプロンプトは今後PowerShellに置き換えられていくそうなので、PowerShell前提で進めます。
- 題材として公式サイトにあるサンプルprotoファイルを使用して、これをTypeScript対応させていきます。
下準備
適当な場所に、新規フォルダを作成し、npm
初期化から typescript
と ts-node
のインストールまで行います。
npm init -y
npm i -D typescript ts-node
今後 src
フォルダにソースを入れていくので、src
フォルダも用意します。
gRPC 実装
では、gRPC 用のモジュール追加やらファイル作成していきます。
どうでも良い話ですが、ずっとgr pc を gr cp だと思っていて、いまだに間違えます。P = Protocolの印象があるからですね…
proto ファイルを作成
前述の通り公式サンプルを使用するので、以下のように proto ファイルを作成します。
今回は proto
フォルダを作成してその中に hello.proto
を作成しました。
syntax = "proto3";
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
冒頭の syntax = "proto3";
は、公式サイトには無いのでご注意下さい。
コード生成
必要な依存関係をインストールします。
npm i -D grpc-tools grpc_tools_node_protoc_ts
参考元の記事ではここでシェルスクリプトを書くのですが、こちとら Windows なので ps1
ファイルです。
proto.ps1
というファイルを作成します。変数は必要であれば適宜変更してください。
windowsでは proto\*
という記述が出来ないみたいで、とりあえず一つのファイルだけを指定しています。
今回はこれで問題ないのですが、おまけ2に複数ファイルを変換できるコマンドを記載しておきます。
$PROTO_SRC="proto\hello.proto"
$PROTO_DEST="$(pwd)\src"
npx grpc_tools_node_protoc --js_out=import_style=commonjs,binary:$PROTO_DEST --grpc_out=$PROTO_DEST --plugin=protoc-gen-grpc=$(npm bin)/grpc_tools_node_protoc_plugin.cmd $PROTO_SRC
npx grpc_tools_node_protoc --plugin=protoc-gen-ts=$(npm bin)/protoc-gen-ts.cmd --ts_out=$PROTO_DEST $PROTO_SRC
ポイントは、コマンドに .cmd
が追加されている事ですね。
これを実行します。
./proto.ps1
すると、src/proto
に以下のファイルが出来ます。
proto
フォルダは自動で作成するようですが、src
フォルダは事前に作っておかないとエラーが出ます。
- hello_grpc_pb.d.ts
- hello_grpc_pb.js
- hello_pb.d.ts
- hello_pb.js
サーバー実装
gRPC サーバーを実装するための依存関係は以下の通りです。
npm i -D @types/google-protobuf
npm i -S grpc google-protobuf
src/app.ts
を作成し、以下の内容を記述します。
import * as grpc from "grpc";
import { IGreeterServer, GreeterService } from "./proto/hello_grpc_pb";
import { HelloRequest, HelloReply } from "./proto/hello_pb";
class HelloService implements IGreeterServer {
public sayHello(
call: grpc.ServerUnaryCall<HelloRequest>,
callback: grpc.sendUnaryData<HelloReply>
): void {
const r = new HelloReply();
r.setMessage("Hello " + call.request.getName());
console.log("receive: " + call.request.getName())
callback(null, r);
}
}
const server = new grpc.Server();
server.bind("0.0.0.0:50051", grpc.ServerCredentials.createInsecure());
server.addService(GreeterService, new HelloService());
server.start();
console.log("server running on 0.0.0.0:50051");
いずれもタイプセーフ、lintにも引っかかりません。気持ちいい。
以下のコマンドでサーバー起動します。
npx ts-node src/app.ts
クライアント実装
同様に、クライアントも実装していきます。
src/client.ts
を作成し、以下の内容を記述します。
import * as grpc from "grpc";
import { GreeterClient } from "./proto/hello_grpc_pb";
import { HelloRequest } from "./proto/hello_pb";
const client = new GreeterClient(
"127.0.0.1:50051",
grpc.credentials.createInsecure()
);
const req = new HelloRequest();
req.setName("John");
client.sayHello(req, function (error, result) {
if (error) console.log("Error: ", error);
else console.log(result.toObject());
});
こちらもタイプセーフでlintクリア。気持ちいい。
以下のコマンドで起動します。
npx ts-node src/client.ts
テスト
PowerShell のプロンプトを2つ立ち上げ、サーバー -> クライアントの順に起動します。
サーバー側に
server running on 0.0.0.0:50051
receive: John
クライアント側に
{ message: 'Hello John' }
が表示されれば成功です。
おまけ: 必要な依存パッケージまとめ
必要な依存関係が、記事中にバラバラしてしまったので、まとめておきます。
npm i -D typescript ts-node grpc-tools grpc_tools_node_protoc_ts @types/google-protobuf
npm i -S grpc google-protobuf
おまけ2: 複数のprotoファイルを変換する
前述の通り windows の PowerShell でワイルドカード指定が出来ないので、ファイル一覧を取得して一つ一つ変換するコマンドを書きました。ゴリ押しです。
$PROTO_DIR="proto"
$PROTO_DEST="$pwd\src"
$LIST=Get-ChildItem ${pwd}/$PROTO_DIR -Name -Include *.proto
function convert($FILE) {
npx grpc_tools_node_protoc --js_out=import_style=commonjs,binary:$PROTO_DEST --grpc_out=$PROTO_DEST --plugin=protoc-gen-grpc=$(npm bin)/grpc_tools_node_protoc_plugin.cmd $PROTO_DIR/$FILE
npx grpc_tools_node_protoc --plugin=protoc-gen-ts=$(npm bin)/protoc-gen-ts.cmd --ts_out=$PROTO_DEST $PROTO_DIR/$FILE
}
foreach($item in $LIST) {
convert($item);
}