入力(.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 と揃っているからです — int64date-timeduration といったフォーマットがファーストクラスで扱え、$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 ドキュメントです。

1

.proto スキーマを貼り付ける

左のエディタにスキーマをドロップしてください。先頭の syntax = "proto3"; はあっても省略しても大丈夫です。パーサはネストした message ブロック、enum 宣言、oneofmap<K, V>、フィールドオプションを処理します。ファイル間の import 文は認識されますがスキップされます — スキーマ同士が参照し合っているならインポート対象の型をインラインで貼り付けてください。

出力ではフィールド名は snake_case のままで、proto3 JSON エンコーダのデフォルト動作に揃えています。gateway を camelCase JSON 名で設定している場合は、出力を一括置換するか gateway の設定を変更してください。

2

OpenAPI 出力を読む

右側: openapi: 3.1.0info ブロック、空の paths: {}、そして components.schemas 配下に message と enum が並ぶ YAML ドキュメントが得られます。ネストした message への参照は葉名に対する $ref: '#/components/schemas/MessageName' を使うので、.proto でネスト型を宣言していてもフラットな参照で動きます。

3

組み込む

ファイルを Swagger UIRedoc のインスタンスに貼って、スキーマがどう見えるか確認しましょう。完全な 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 ファイルにあります。バックエンドの同僚が Ordershipping_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 に揃えており、int64date-timeduration などのフォーマットがきちんと定義され、$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 マッピングint64uint64fixed64sfixed64sint64 を 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 がデフォルトでそうエンコードするからです。.protoorder_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.Timestamptype: string, format: date-timeDurationtype: string, format: durationEmpty は空オブジェクト、Any は汎用オブジェクト、Value{}(任意の JSON 値)として出力されます。それ以外の Google well-known types については、コンバーターは現状、葉名への $ref を出します — 自分でそれらのスキーマを定義するか、参照を正しいプリミティブ型に置き換えてください。

関連ツール

Protobuf と OpenAPI/JSON Schema を扱っているなら、これらは相性が良いです: