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