Protobuf から OpenAPI 変換ツール
.proto スキーマを貼り付けてください。message と enum が components.schemas として出力された OpenAPI 3.1 YAML ドキュメントが手に入ります — そのまま spec に貼り付けられます。
入力(.proto スキーマ)
出力(OpenAPI YAML)
このツールでできること
Protocol Buffers スキーマがあって、フロントエンド・パートナー・QA チームが同じ形を OpenAPI ドキュメントで欲しいと言ってきた — gRPC サービスが grpc-gateway のような仕組みで HTTP にも公開されているからです。本番環境ならビルドに protoc-gen-openapiv2 を組み込むのが正解ですが、共有用や Swagger UI に貼るだけのちょっとした spec が欲しいだけならオーバースペックです。このコンバーターはその仕事の「スキーマ部分」だけをブラウザで完結させます — .proto を貼り付け、YAML をコピーすれば、有効な OpenAPI components が手に入ります。
出力は最小限ながら有効な OpenAPI 3.1.0 ドキュメントで、空の paths: {} と、Protobuf の message・enum ごとに 1 エントリが components.schemas 配下に並びます。OpenAPI 3.1 を選んでいるのは、データモデルが JSON Schema 2020-12 と揃っているからです — int64、date-time、duration といったフォーマットがファーストクラスで扱え、$ref も他のキーワードと並べて書ける、3.0 時代の書き換えハックが要らないバージョンです。
型のマッピングは proto3 JSON マッピング に従います: 32 ビット整数は type: integer, format: int32、64 ビット整数は type: string, format: int64 に数値パターン付き(JSON の数値は 2^53 を超えると精度が落ちるため)、repeated T は配列、map<K, V> は additionalProperties 付きのオブジェクト、フィールド名は snake_case のままで、サーバが実際にシリアライズするものとスキーマが一致します。enum は type: string として、値名の enum リストを伴って出力されます — proto3 JSON と同じです。コンバーターはすべてブラウザで動作し、スキーマがページから外に出ることはありません。
使い方
3 ステップ。出力は Swagger UI に貼ったり、既存の spec にマージしたりできる YAML ドキュメントです。
.proto スキーマを貼り付ける
左のエディタにスキーマをドロップしてください。先頭の syntax = "proto3"; はあっても省略しても大丈夫です。パーサはネストした message ブロック、enum 宣言、oneof、map<K, V>、フィールドオプションを処理します。ファイル間の import 文は認識されますがスキップされます — スキーマ同士が参照し合っているならインポート対象の型をインラインで貼り付けてください。
出力ではフィールド名は snake_case のままで、proto3 JSON エンコーダのデフォルト動作に揃えています。gateway を camelCase JSON 名で設定している場合は、出力を一括置換するか gateway の設定を変更してください。
OpenAPI 出力を読む
右側: openapi: 3.1.0、info ブロック、空の paths: {}、そして components.schemas 配下に message と enum が並ぶ YAML ドキュメントが得られます。ネストした message への参照は葉名に対する $ref: '#/components/schemas/MessageName' を使うので、.proto でネスト型を宣言していてもフラットな参照で動きます。
組み込む
ファイルを Swagger UI や Redoc のインスタンスに貼って、スキーマがどう見えるか確認しましょう。完全な spec にしたいなら、grpc-gateway のルートを指す本物の paths エントリ(あるいは手書きのもの)を追加し — $ref で #/components/schemas/Order を参照すればそれで動きます。
こんなときに本当に時短になります
フロントエンドやパートナーチームとスキーマを共有する
フロントエンドチームが gRPC トランスコード済みのサービスを使っています。型ジェネレーターや API エクスプローラに食わせるために OpenAPI ドキュメントが欲しいと頼んできました。.proto を貼って YAML をコピーして送るだけ。彼らは自分たちのツーリングが既に理解できるフォーマットでスキーマを受け取れます。
新しい HTTP トランスコード API の spec を立ち上げる
buf 付きの grpc-gateway 経由で gRPC と HTTP の両方で動かす新サービスを立ち上げるところです。components/schemas セクションは機械的な作業 — このツールで生成し、その後で正しいルートテンプレートを使った paths を手書きします。全部手で組むより速いです。
Swagger UI を .proto の変更に追従させる
チームでは人が読む API ドキュメントに Swagger UI を使っていますが、真実の源は .proto ファイルにあります。バックエンドの同僚が Order に shipping_address を追加した — ここで schemas セクションを再生成し、spec に貼り戻して、ドキュメントの更新を出します。
codegen の出力をサニティチェックする
ビルドの一環として protoc-gen-openapiv2 を回したら 4000 行の YAML が出てきました。特定の message が正しい形になっているか確認したい場合は、その .proto ファイルだけをここに貼って比較してください。きれいな OpenAPI 3.1 マッピングがどう見えるか、手早い参照になります。
よくある質問
なぜ OpenAPI 3.0 ではなく 3.1 なのですか?
OpenAPI 3.1 はデータモデルを JSON Schema 2020-12 に揃えており、int64、date-time、duration などのフォーマットがきちんと定義され、$ref を他のキーワードと並べて書いても 3.0 時代の不格好な回避策が要りません。最近のツール(Redoc、Swagger UI 5、Stoplight、Spectral)は問題なく 3.1 を理解します。レガシーツーリングのためにどうしても 3.0 出力が必要なら、ドキュメント先頭の openapi: 3.1.0 を変更してください — それ以外の部分は十分互換性があり、たいてい検証は通ります。
なぜ int64 が数値ではなく文字列で出力されるのですか?
JSON の数値は実質 IEEE-754 の double で、2^53 を超えると精度が失われます。公式の proto3 JSON マッピング は int64、uint64、fixed64、sfixed64、sint64 を JSON 文字列でエンコードすると規定しています。なので OpenAPI スキーマでは type: string, format: int64, pattern: "^-?[0-9]+$" を使い、サーバが実際に送るものに合わせています。値が小さいと割り切って type: integer を使いたい場合は、出力を一括置換してください。
なぜ paths は空なのですか?
.proto ファイルが記述するのは message とサービスであって、HTTP ルートではありません。HTTP トランスコード(パス、メソッド、クエリ、ボディ)は別途設定するものです — 通常は google.api.http アノテーションや grpc-gateway のオプションで指定します。これらはパースしないので、paths: {} はあなたが埋めるために空のままにしてあります。components/schemas の部分が再利用可能で機械的な半分 — それがこのツールの提供するものです。
なぜフィールド名が snake_case なのですか?
proto3 JSON がデフォルトでそうエンコードするからです。.proto の order_id は、camelCase オプション(一部のエンコーダの preserve_proto_field_names や gateway 側のフラグ)を有効にしない限り、JSON ワイヤーフォーマットでは "order_id" としてシリアライズされます。OpenAPI スキーマを snake_case のままにしておけば、サーバが実際に送る JSON と一致します。スタックが camelCase なら、出力を一括置換してください。
ネストした message はどう扱われますか?
各 Protobuf message は、葉名(commerce.v1.Address ではなく Address)をキーに components.schemas 直下のトップレベルエントリになります。参照には $ref: '#/components/schemas/Address' を使います。違うパッケージで葉名が同じ message が 2 つあると、後者が前者を上書きします — ドキュメントを分けるか .proto でリネームしてください。
google.protobuf の well-known types はちゃんと理解しますか?
いくつかは。google.protobuf.Timestamp は type: string, format: date-time、Duration は type: string, format: duration、Empty は空オブジェクト、Any は汎用オブジェクト、Value は {}(任意の JSON 値)として出力されます。それ以外の Google well-known types については、コンバーターは現状、葉名への $ref を出します — 自分でそれらのスキーマを定義するか、参照を正しいプリミティブ型に置き換えてください。
関連ツール
Protobuf と OpenAPI/JSON Schema を扱っているなら、これらは相性が良いです: