bufで.protoファイルからNestJSのソースコードを生成する
対象読者
- gRPCを使用してプログラム間通信を行っている。
- コマンドラインでprotocの生成コマンドを打っている。
- 開発言語はTypescriptでNestJS & NodeJSを利用している。
今まで
プロトコルバッファの.protoファイルをprotocコマンドで開発言語のインターフェイスファイルを生成していました。
protoc \
--ts_proto_opt=esModuleInterop=true,env=node,nestJs=true,forceLong=string \
--plugin=../../node_modules/.bin/protoc-gen-ts_proto \
--ts_proto_out=src/generated src/proto
@bufbuild/buf
bufというツールがあります。
設定ファイル(buf.gen.yaml)をもとにprotoファイルから開発言語用のインターフェイスファイルを作成してくれるというものです。
一度設定ファイルを作成しておけば、いちいちコマンドラインにパラメータを入力しなくても済みます(protocでもbashファイルを作ってそれを実行されていると思いますが)。
インストール
gRPCでの通信ですので、プロジェクトにgRPCのモジュールがインストールされていることが前提です。
yarn add @grpc/grpc-js @grpc/proto-loader
Typescript言語のインターフェイスを生成するためのプラグインとして、ts-protoを使用するため、これもインストールしておきます。
yarn add -D ts-proto
Node.JSに@bufbuild/bufパッケージを開発用パッケージとしてインストールします。私はyarnを使用してますので、yarnによる記述をします。
yarn add -D @bufbuild/buf
bufの初期化
bufの初期化を行い、buf.yamlを生成します。
buf config init
buf.yamlというファイルが出来上がります。
# For details on buf.yaml configuration, visit https://buf.build/docs/configuration/v2/buf-yaml
version: v2
lint:
use:
- STANDARD
breaking:
use:
- FILE
このファイルでprotoファイルの場所やlintのルールや破壊的変更の検出方法について指定することができます。
私のプロジェクトではsrc/proto配下にprotoファイルを作成するため、buf.yamlのmodules.pathにprotoファイルの場所を指定します。
# For details on buf.yaml configuration, visit https://buf.build/docs/configuration/v2/buf-yaml
version: v2
modules:
- path: src/proto
lint:
use:
- STANDARD
breaking:
use:
- FILE
開発言語のインターフェイスファイルを生成するための設定ファイル(buf.gen.yaml)を作成します。
version: v1
plugins:
- name: ts-proto
out: src/generated
opt: esModuleInterop=true,env=node,nestJs=true,forceLong=string
path: ../../node_modules/.bin/protoc-gen-ts_proto
Typescript言語のインターフェイスファイルを生成するためのプラグインはts-protoを使用しているため、plugins.nameにts-protoを指定します。
出力先はsrc/generatedです。生成オプションをoptに記述します。Typescript言語のファイルを生成しますので、tsconfig.jsonの設定に合わせたほうがいいです。私はesModuleInterop=trueにしているので、optも同じにしています。
NestJsを使用しているので、nestJs=trueを追加しています。これを入れないとインターフェイスのメソッドがPascaleケースで生成されます。nestJs=trueにするとキャメルケースで出力されます。
nestJs=trueなし
export interface OrderService {
CreateOrder(request: CreateOrderRequest): CreateOrderResponse;
}
nestJs=trueあり
export interface OrderService {
createOrder(request: CreateOrderRequest): CreateOrderResponse;
}
forceLong=stringはint64で定義したプロパティをstringで出力します。
message Order {
int32 id = 1;
int64 totalMoney = 2;
}
出力結果
export interface Order {
id: number;
totalMoney: string;
}
Typescriptのnumberはint64の範囲より狭いため、numberでは受けず、一旦文字列に変換します。
受け取った文字列はbig.jsなどの数値パッケージでBig型に変換して演算を行う様にしています。
プラグインのパスはpathに記述します。モノレポで開発していますので、ルートフォルダにあるnode_modules配下にあるprotoc-gen-ts_protoを指定します。
path: ../../node_modules/.bin/protoc-gen-ts_proto
lint
以下のコマンドでlintができます。
buf lint
ファイルの生成
開発言語のファイルを生成するには、以下のコマンドを使用します。
buf generate
これでoutに指定したフォルダーにファイルが出力されます。
まとめ
設定ファイルがあることでどの様なファイルがどこからどこへ出力されるかが一目瞭然になります。
CIでの設定もしやすくなるかもしれません。あまり変わらないかな?
コマンドラインをbashにするのであれば、人によってファイル名が変わるので、統一感はありませんが、Typescript言語での開発であれば、どのプロジェクトでも生成にはbuf generateで行えるという統一感はあります。
どの様なサービスでもdocker-compose.yamlに記述しておけばdocker compose up -dで起動でいるといった統一感がbufにはあると思うんです。言い過ぎかな?
Discussion