2023年02月02日
テクノロジー

ProtobufでREST APIを快適に開発する方法のご紹介

こんにちは、ティアフォーで認証認可基盤を開発している澤田です。最近取り入れたProtobufで、素晴らしいREST APIの開発体験をしたのでご紹介します。

 

実現したかったこと

マイクロサービス間連携のAPI開発において、以下の条件を満たすやり方を探していました。


  • スキーマを最初に定義してリクエストとレスポンスの型が自動で生成される
  • ドキュメント(openapi.yaml)が生成される
  • バリデーションが定義できて、その実装が自動で生成される

実現方法

Go言語で開発する場合はgo-swaggerでも実現できますが、本記事では、Protobufで実現できるgRPC Gatewayとprotoc-gen-validate (PGV)を使った方法をご紹介します。


gRPC Gateway

https://github.com/grpc-ecosystem/grpc-gateway


gRPC GatewayはProtobufの定義からREST APIのプロキシを生成してくれるプラグインです。gRPCサーバの前段に置くことで、REST APIのインターフェイスを提供することができます。


20220202 Blog Image 01

gRPC Server の前段に gRPC Gateway を置くパターン


単にREST APIを開発する場合は、gRPCサーバは不要なので、gRPC Gatewayにサービスの実装を登録することができます。


20220202 Blog Image 02


gRPC Server を置かずに gRPC Gateway だけのパターン


以下の例のように、Protobufから生成されたRegisterXXXHandlerServerを利用すれば、Gatewayにサービスの実装を登録することができます。


20220202 Blog Image 03


ご参考までにProtobufの定義も載せておきます。message定義の中で必須のフィールドを指定しておくと、swaggerとして吐き出されるときにrequiredとして表現されます。設定を忘れやすいところではありますが、Goの場合は生成したopenapi.yamlからAPI Clientを自動生成し、インテグレーションテストの仕様により必須でないフィールドはポインタになるため、設定を忘れていたことに気づけます。


20220202 Blog Image 04


ちなみに、生成されるドキュメントはOpenAPI v2なので、v3の形式で欲しい場合は以下のようなツールを使ってさらに変換をかける必要があります。


https://github.com/Mermade/oas-kit/blob/main/packages/swagger2openapi/README.md


また、openapi.yamlからAPI Clientの自動生成はoapi-codegenを利用しています。


https://github.com/deepmap/oapi-codegen


protoc-gen-validate(PGV)

https://github.com/envoyproxy/Protobufc-gen-validate


ProtobufからgRPCのメッセージバリデーションを生成してくれるプラグインです。アノテーションでバリデーションルールを表現できます。


20220202 Blog Image 05


gRPCサーバがある場合は、Interceptorでバリデーションできますが、サービスを登録したgRPC Gatewayで使う場合はInterceptorの出番がなく、またHTTPのMiddlewareレイヤではバリデータが使用できません。そのため、Handler内で request.Validate() メソッドを呼ぶ必要があります。


愚直にHandlerの処理でバリデーションメソッドを呼んでもいいのですが、私のチームでは、専用のレイヤでバリデーションするようにしました。コード量は増えてしまいますが、バリデーションの実装忘れは発生しにくいと思います。


BufでProtobufをビルドする

REST APIに限った話ではないのでテーマから少し脱線してしまいますが、開発体験を良くしてくれたBufについても簡単にご紹介させてください。


BufはProtobufの依存管理やprotocで実行していたコマンドをいい感じにまとめてくれるツールです。これを使うことで、 依存管理の悩みから開放され、protocコマンドで長たらしく書いていたものをbuf generateとシンプルにまとめることができます。


https://docs.buf.build/introduction


protoディレクトリをprotoファイルの置き場とした場合、以下のような構成になります。


RepositoryRoot
├── buf.gen.yaml // protoc コマンドの引数ここで定義する
├── buf.work.yaml // workspaceを指定。この場合は proto ディレクトリを指定する
└── proto
├── buf.lock // buf.yaml を作成後、 buf mod update コマンドで自動生成される
├── buf.yaml // .proto ファイルで使用されている依存の定義
└── example
└── echo.proto

 

BSR(Buf Schema Registry)

BufはBSRというProtobuf(Repository)とプラグインのレジストリを提供しています。


https://buf.build/


Protobufを提供しているRepositoryは充実していますが、プラグインはBSRに登録されていないことがあります。例えば、protoc-gen-validateのRepositoryはありますが、2022年1月現在プラグインは提供されていないので、プラグインが実行可能なDockerfileを自分で用意、BSRに登録し、使えるようにする必要があります。

 

まとめ

今回は、Protobufを使ってREST APIを開発する方法と、ProtobufのツールであるBufについてご紹介しました。バリデーションまでできるスキーマ駆動開発の方法はかなり少ないと感じており、今後しばらくは有力な選択肢の一つになるのではないかと思っています。


オープンソースのソフトウェアを一緒に開発していきませんか?

 

ティアフォーでは、「自動運転の民主化」というビジョンに共感を持ち、自らそれを実現する意欲に満ち溢れた新しい仲間を募集しています。

 

キャリアページ

 

Media Contact

pr@tier4.jp

 

Share the post

LinkedIn | Twitter | Facebook | Instagram

 

More

Autoware—Github | The Autoware Foundation