Bufで構築する、現実的なスキーマファーストなREST APIの運用
BufというProtocol Buffersを使ったAPIを簡単に作成可能なライブラリも存在する。
Bufの言いたいだろうことをめちゃくちゃ意訳すると
- 静的型付け言語は広く利用されていますが、API設計においてはまだスキーマ駆動のアプローチが積極的には採用されていない状況です(RESTfulやJSON、HTTPプロトコルなどが主流)。
- しかし、APIのスキーマが自由度が高すぎるため、保守性の低下が懸念されています。初期のスタイルガイドも無視され、コードレビューに頼るケースが増えています。
- スキーマを持つことで、クライアントとAPI間の理解が容易になり、共通の理解を得やすくなります。特に、静的型付け言語(TypeScriptやGolangなど)を用いている場合、型を定義できるため、スキーマ開発のメリットが一層増します。
- Bufは、スキーマ駆動開発においてProtocol Buffersが最適な選択肢であると考えており、学習コストを吸収できるツールを提供しています。
buf CLIにより互換性を維持し、ベストプラクティスに準拠した一貫性のあるProtobuf APIを作成可能。
また、Linterや互換性を強制する変更検出機能も存在し、実装者間でソースコードの書き方とある程度統一可能。
$ brew install bufbuild/buf/buf
go mod initのようなノリで初期化可能。
lintでDEFAULTは厳格ですが、exceptは極力入れずに新規プロジェクトではできればこれで運用したい。
# これでbuf.yamlが生成される
$ buf mod init
$ cat buf.yaml
version: v1
breaking:
use:
- FILE
lint:
use:
- DEFAULT
下記のチュートリアルでイメージをふくらませる。
下記の公式サンプルを取ってきて、~/../buf-tour/start/getting-started-with-buf-cli/proto
に移動し、buf mod init
する。
現在は、googleとpetに関するprotoの定義が存在。
buf.yaml
はproto配下に配置すれば良さそう。
.
├── buf.yaml
├── google
│ └── type
│ └── datetime.proto
└── pet
└── v1
└── pet.proto
buf.yamlが存在するprotoディレクトリにprotoのファイルを集めておくと良いとのこと。
コード生成するために、protoディレクトリの上の階層で生成を定義するbuf.gen.yaml
を作成する。
version: v1
managed:
enabled: true
go_package_prefix:
default: github.com/bufbuild/buf-tour/gen
plugins:
- plugin: buf.build/protocolbuffers/go
out: gen
opt: paths=source_relative
- plugin: buf.build/connectrpc/go
out: gen
opt: paths=source_relative
↑の細かい理解は後回しにして、buf.gen.yaml
を置いた階層で、buf generate proto
を実行すると、genに自動生成コードが吐き出される。
pet.connect.go
は、pet.protoのほうにgRpcメソッドが書かれているため、同社のConnectというgRPC互換のHTTP API生成ライブラリのこと。
Connectのドキュメントも覗いたが、結局bufコマンドを使用しているので基本はほぼ同じ。
これで、protocのときのような長いコマンドを書かずとも、buf generate
で簡単に生成可能。Dockerfileとdocker-compose.yamlのような関係性と似ており、MakeFileなどで定義せずともチーム内で平準化して使えそうだと思った。
.
├── buf.gen.yaml
├── gen
│ ├── google
│ │ └── type
│ │ └── datetime.pb.go
│ └── pet
│ └── v1
│ ├── pet.pb.go
│ └── petv1connect
│ └── pet.connect.go
└── proto
├── buf.yaml
├── google
│ └── type
│ └── datetime.proto
└── pet
└── v1
└── pet.proto
チュートリアルにあるprotoファイルはいくつか修正できる部分があるので、ここはbuf lintで検出可能。
- petIDはスネークケースにする
- gRpcのPetStoreはPetStoreServiceにする
$ buf lint proto
proto/google/type/datetime.proto:17:1:Package name "google.type" should be suffixed with a correctly formed version, such as "google.type.v1".
proto/pet/v1/pet.proto:42:10:Field name "petID" should be lower_snake_case, such as "pet_id".
proto/pet/v1/pet.proto:47:9:Service name "PetStore" should be suffixed with "Service".
googleの方は下記の内容であり、ここの警告を無視したい場合は、buf.yaml
にignoreを入れる。
あとは↑の定義を使いサーバーを立てれば疎通できる。
参考
Discussion