🦁

【Go】buf genを使ってopenapi v2をprotoからpostmanのcollectionを自動生成する

に公開

概要

プロジェクトでgRPCを用いたGo言語バックエンド開発において、人数増加に伴い、現在実装されているrpcのAPIテスト用のpostmanのcollectionを自動で生成できるようにしたい。

前提

技術セット

  • gRPC
    • gRPCは、Googleが開発したオープンソースのRPC(Remote Procedure Call)フレームワーク
  • buf
    • bufは、Protocol Buffers(protobuf)のビルドツールとコード生成を現代的に改善したツール
  • buf-connect
    • buf-connectは、Protocol Buffers(protobuf)を使用してgRPCサービスをHTTP/1.1およびHTTP/2上で実行するためのプロトコルとツールキット。従来のgRPCと比べて、より簡単にWebアプリケーションと統合できる

ディレクトリ構成

.
├── Makefile
├── buf.gen.yaml
├── buf.lock
├── buf.yaml
├── docs
│   └── apidocs.swagger.json
├── gen
│   └── proto
│       ├── example.pb.go
│       └── protoconnect
│           └── example.connect.go
├── go.mod
├── go.sum
├── main.go
└── proto
    └── example.proto

今回はgo mod initで生成されるmodule名をgenswaggerと適当においておきます。

buf genとは

https://buf.build/docs/generate/tutorial/

buf genは、Buf CLIのコマンドで、Protocol Buffers(Protobuf)ファイルからコードを生成するために使用されます。これは、従来のprotocコマンドの直接的な代替として機能し、コード生成のプロセスを簡素化します。

buf.yaml,buf.gen.yamlを書く

version1とversion2がありますが今回はversion1で書きます

buf.yaml

version: v1

buf.gen.yaml

version: v1
managed:
  enabled: true
  go_package_prefix:
    default: genswagger/gen
    except:
      - buf.build/googleapis/googleapis
plugins:
  - plugin: buf.build/connectrpc/go:v1.11.0
    out: ./gen
    opt:
      - paths=source_relative
  - plugin: buf.build/protocolbuffers/go:v1.30.0
    out: ./gen
    opt:
      - paths=source_relative
  - plugin: buf.build/grpc-ecosystem/openapiv2:v2.24.0
    out: ./docs
    opt:
      - allow_merge
  • buf.build/connectrpc/go: Connect-Go用のコード生成プラグイン。gRPCサービスに対応するConnect-Go実装を生成する。
  • buf.build/protocolbuffers/go: Protocol BuffersのGo言語用コード生成プラグイン。メッセージ型やサービス定義に対応するGoのコードを生成する。
  • buf.build/grpc-ecosystem/openapiv2: OpenAPI v2(Swagger)仕様のドキュメントを生成するプラグイン。gRPCサービスをRESTful APIとして文書化します。今回はswaggerとしては使用せず、postmanにimportするjsonファイルとして使用する。

google/api/annotations.protoのmoduleをimportする

google/api/annotations.proto を利用してHTTPエンドポイントを指定するため、以下の手順でモジュールを追加します。

  1. buf.yamlに記述を追加

    version: v1
    deps:
      - buf.build/googleapis/googleapis
    
  2. モジュールをインポートする:
    Bufモジュールとして googleapis をインポートします。

buf mod update

成功するとbuf.lockが生成されます

# Generated by buf. DO NOT EDIT.
version: v1
deps:
  - remote: buf.build
    owner: googleapis
    repository: googleapis
    commit: ...
    digest: ...

protoに必要な記述を追加する

  1. まず最初に初期状態のprotoファイルはこんな感じで、適当なrpcを用意します。
syntax = "proto3";

package example;

service ExampleService {
  rpc GetExample (ExampleRequest) returns (ExampleResponse) {}

message ExampleRequest {
  string id = 1;
}

message ExampleResponse {
  string name = 1;
}

  1. HTTPルールを追加する。

各RPCメソッドにHTTPエンドポイントを追加するために、google/api/annotations.proto を使用します。

syntax = "proto3";

package example;

import "google/api/annotations.proto"; // 追加

service ExampleService {
  rpc GetExample (ExampleRequest) returns (ExampleResponse) {
  // ここにオプションを追加
    option (google.api.http) = {
      post: "/v1/example/{id}"
      body: "*"
    };
  }
}

message ExampleRequest {
  string id = 1;
}

message ExampleResponse {
  string name = 1;
}

grpc-gatewayを使っている方なら馴染のある書き方かもしれないですが、returnsのあとの{}にオプションを追加することで設定が可能になります。

makefileでコマンドを定義する

.PHONY: gen
generate:
	@buf generate

.PHONY: update
update:
	@buf mod update

上記のMakefileに2つの主要なコマンドを定義しています:

  • make gen
    • buf generateコマンドを実行し、buf.gen.yamlで定義された内容を自動生成するためのコマンド。今回はconnectrpcprotocolbuffersopenapiv2のコードをoutで指定したディレクトリに自動生成します。
  • make update
    • buf mod updateコマンドを実行し、buf.yamlで定義された依存関係を更新するコマンド。buf.genのdepsで指定したパッケージを外部からimportしてbuf.lockに保存します。今回はgoogle/api/annotations.protoを外部モジュールとしてインポートするためmake genする前にこちらを行う必要があります

これらのコマンドは.PHONYターゲットとして定義されており、実際のファイル名と競合しないようになっています。@プレフィックスは、コマンド実行時にコマンド自体を表示しないようにするための指定です。

https://buf.build/bufbuild/connect-go?version=v1.10.0

自動生成したjsonをpostmanにimport

  1. postmanのworkspaceを作成してimportボタンを押す

  2. Finderなどで自動生成したファイルを選択してimportする

これでpostman用のrpcの雛形ができました。

その他設定

リクエストフィールド

デフォルトで型の名前が入っていてこれを書き換えることで使用することができます。

baseUrl

クエリやURLパラメーターの部分はprotoに入力する必要がありますが、baseのURL部分は{{baseUrl}}という変数として定義されています。
これはpostmanでimportしたtop層のコレクションのVariablesで設定が可能になります。

Authorization設定

baseUrlと同じでコレクションのtop層のAuthorizationで共通設定可能です

導入のメリット・デメリット

メリット

  • 入れ替わりの激しいprojectでもAPIドキュメントとして提供できる
  • postmanを使ってバックエンド開発する人向けにはとにかく便利

デメリット

  • 更新がある際に変更点だけを更新するのが難しい
  • protoファイルの可読性が下がる

まとめ

こんな感じで、開発ツールとしてpostman用のjsonファイルをprotoから自動生成できました。
本来openapiはswaggerを表示するためのものだったりしますが、postman用に作成することもできるので便利ですね。

Discussion