gRPCでサービスを組んだあとに、社内の既存クライアントや外部連携先から「JSONのHTTP APIで呼びたい」と言われる場面があります。gRPC-Gatewayは、そのためにサーバをもう一本書き起こす必要をなくす仕組みです。protoファイルのアノテーションを読み取り、RESTful HTTP呼び出しをgRPCへ変換するリバースプロキシを生成します。ただし変換規則には決まった型があり、扱えるストリーミングの範囲にも線が引かれています。この記事では、アノテーションで何が決まるのか、生成物の構成、OpenAPI同時出力の現在地、採用を見送るべき条件までを2026年9月時点の一次情報で整理しました。
まとめ|gRPC-Gatewayを採用してよい条件と先に固める生成の構成
先に結論を置きます。gRPC-Gatewayが効くのは、gRPCサービスが先にあり、そこへ手を入れずにJSONのHTTP入口だけを足したい場合です。生成されたプロキシがHTTPを受けてgRPCへ変換するため、gRPCサーバ側はREST入口の存在を知らずに済みます。RESTが主でgRPCを持っていないなら、持ち込む理由はありません。
採用前に固めておく項目は三つあります。ひとつはアノテーションの設計で、どのRPCにどのHTTPメソッドとパスを割り当て、リクエスト本文に何を載せるかをprotoファイル上で決める作業です。二つめは生成物の管理で、protocプラグインが四つ前後並ぶため、版を固定してCIで作り直せる形にします。三つめは配置で、gRPCサーバと同居させるか別プロセスのプロキシとして立てるかを最初に選びます。
版の現在地も判断材料に入ります。GitHubの最新リリースはv2.30.0(2026年8月5日公開)で、その前がv2.29.0(2026年4月15日)、v2.28.0(2026年2月17日)です。v2.30.0ではOpenAPI 3.1を出力するprotoc-gen-openapiv3が加わりましたが、公式READMEはこれをアルファと明記し、本番で安定して使える選択肢はprotoc-gen-openapiv2のほうだと述べています。
gRPC-Gatewayの定義とprotoアノテーションからRESTが生える仕組み
gRPC-Gatewayは新しい通信プロトコルではなく、protocのプラグインです。protobufのサービス定義を読み込み、RESTful HTTPリクエストをgRPCへ変換するリバースプロキシのコードを吐き出します。
protoc-gen-grpc-gatewayが生成するリバースプロキシの中身
生成されるのはGoのソースコードで、HTTPのルーティング表と、受け取ったリクエストをprotobufメッセージへ組み立ててgRPCクライアントとして呼び出す処理が入っています。実行時に動くのは、HTTPサーバとgRPCクライアントを兼ねた一枚のレイヤです。gRPCサービス本体には変更が入りません。
配置は二通りです。生成コードを既存のGoサーバへ組み込んで同じプロセスで動かすか、独立したプロキシとして別プロセスで立てるかで、前者は追加のホップが無く、後者はgRPCサーバがGo以外の言語でも使えます。gRPC本体の仕組みやRESTとの違いは、gRPCとは?仕組み・RESTとの違い・UDP/TCPの扱いを実装例つきで解説を土台として読んでください。
google.api.httpのHttpRuleで決まるHTTPメソッドとパス
変換規則の設計図はprotoファイル側にあり、RPCのオプションとしてgoogle.api.httpを書き、HttpRuleでHTTPメソッドとURLパスを指定します。
rpc GetMessage(GetMessageRequest) returns (Message) {
option (google.api.http) = {
get: "/v1/messages/{message_id}"
};
}
HttpRuleのpatternはget・put・post・delete・patchの5種類がoneofとして定義され、これら以外のHTTPメソッドはcustomフィールドで指定します。パステンプレートの構文も決まっており、アスタリスク1個が単一セグメント、2個がゼロ個以上のセグメントに対応し、2個のほうは末尾にしか置けません。波かっこで囲んだ部分が変数となり、対応するリクエストフィールドへ束ねられます。Protocol Buffersとは?protoファイルの書き方と採用判断を実装者向けに解説で扱うスキーマ定義の作法が、この設計の前提になります。
bodyの指定でパス・リクエスト本文・クエリの割り振りが変わる規則
リクエストのフィールドがどこへ載るかは、bodyの書き方で機械的に決まります。パステンプレートが参照するものはURLパスへ、bodyが参照するものはHTTPリクエスト本文へ、残りはすべてURLクエリパラメータへ回る、という三分類です。
| bodyの書き方 | リクエスト本文 | クエリパラメータ |
|---|---|---|
| フィールド名を指定 | そのフィールドのみ | 残りの全フィールド |
| アスタリスク1個 | パス以外の全フィールド | 生成されない |
| 省略する | 持たない | パス以外の全フィールド |
この三択がそのままAPIの見た目を決めます。作成系のRPCでリクエスト全体をJSONで受け取りたければアスタリスク1個、取得系で本文を持たせたくなければ省略、という対応です。応答側にはresponse_bodyがあり、応答メッセージの特定フィールドだけを本文に載せられます。なお、繰り返しのメッセージフィールドはクエリパラメータへ割り当てられません。一覧取得のフィルタ条件を凝った構造で受けたい場合、この制約に当たります。
生成物の構成|四つのプラグインとgo.modでの版固定の進め方
導入でつまずきやすいのは、変換規則よりもツールチェーンの管理です。protocプラグインが複数並び、それぞれ別のリポジトリで版が動きます。
protoc-gen-goから始まる四つの生成物と役割の分担
標準的な構成では、protoc-gen-goがメッセージ型を、protoc-gen-go-grpcがクライアントとサーバのスタブを、protoc-gen-grpc-gatewayがリバースプロキシを、protoc-gen-openapiv2がAPI定義を出力します。前2つはprotobufとgRPC本体の側、後ろ2つがgRPC-Gatewayの側です。
この分担を押さえておくと、生成に失敗したときの切り分けが早くなります。メッセージ型が出ないならprotoc-gen-go、RESTのハンドラだけ出ないならアノテーションかprotoc-gen-grpc-gateway、という確認順が目安です。1つのRPCにアノテーションを書き忘れた場合、そのRPCはHTTP入口を持たないだけでgRPCとしては動くため、REST側から404が返るのに単体テストは通る、という状況が生まれます。
Go 1.24のtoolディレクティブとbufでの版固定の要点
プラグインの版がメンバー間でずれると、生成コードの差分がレビューを埋め尽くします。Go 1.24以降はgo.modのtoolディレクティブで実行可能な依存を管理でき、公式READMEもこちらを推奨する書き方に切り替わりました。従来のtools.goに空インポートを並べる方式は、1.24へ上げていないプロジェクト向けの回避策という位置づけです。
protocを直接叩かずbufを使う運用も一般的で、buf.gen.yamlにプラグインと出力先を並べておけばbuf generateだけで一式が揃います。どちらにせよ、生成物をコミットするのかCIで毎回作り直すのかは決めておいてください。中途半端な併用は、手元とCIで版の異なる生成物を生みます。
OpenAPI同時出力|v2の安定版とv3アルファ版の切り分け
同じアノテーションからAPI定義を出力できる点は、この仕組みを選ぶ大きな理由になります。従来からあるprotoc-gen-openapiv2はSwagger 2.0を出力し、v2.30.0でprotocツールチェーン側へ移されました。あわせてOpenAPI 3.1を出力するprotoc-gen-openapiv3と、複数のOpenAPI v3ドキュメントを統合するopenapiv3-mergeが追加されています。
ただしREADMEの警告どおり、protoc-gen-openapiv3はアルファです。oneofやラッパー型、列挙型あたりの出力形が今後変わりうると明記され、マイナーリリース間で出力バイト列が安定する前提を置かないよう求めています。生成したOpenAPIを外部へ配布する、あるいはクライアント生成の入力にするなら、当面はprotoc-gen-openapiv2に寄せてください。
通信仕様の詰まり所|ヘッダ変換とエラー対応とストリーミング制約
生成まで進んだあと、実際の通信で引っかかるのはヘッダとエラーとストリーミングの3か所です。いずれも既定の挙動が決まっており、変えるための入口も用意されています。
Grpc-Metadata-接頭辞とgrpcgateway-接頭辞の変換規則
HTTPヘッダはそのままgRPCメタデータへ渡るわけではありません。Grpc-Metadata-で始まるヘッダは、grpcgateway-を前置してgRPCメタデータへ写されます。IANAに登録された恒久ヘッダも同じ扱いです。HTTPのAuthorizationヘッダはauthorizationとして、送信元IPはX-Forwarded-For、ホストはX-Forwarded-Hostとして載ります。
デッドラインはGrpc-Timeoutヘッダで指定でき、HTTP側から入ってきた値がgRPCのタイムアウトになります。既定の対応表で足りない場合は、WithIncomingHeaderMatcherとWithOutgoingHeaderMatcherで写す条件を書き換えます。注意したいのは、HTTPヘッダをRPCのメソッドパラメータとして受け取る使い方と、トレーラーメタデータの扱いが、どちらも対応予定なしと明記されている点です。既存のREST APIが認証以外の業務パラメータをヘッダで受けているなら、その仕様は持ち込めません。
gRPCコードからHTTPステータスへの既定マッピング対応一覧
エラーの見え方は、gRPCのステータスコードをHTTPへ翻訳した結果です。実装はランタイムのHTTPStatusFromCodeで、対応は固定されています。
| gRPCコード | HTTPステータス | 読み替えの注意 |
|---|---|---|
| InvalidArgument | 400 | 入力検証はここに集まる |
| FailedPrecondition | 400 | 412にはならない |
| Unauthenticated | 401 | 認証の失敗 |
| PermissionDenied | 403 | 認可の失敗 |
| NotFound | 404 | 資源の不在 |
| AlreadyExists/Aborted | 409 | 競合として同一視される |
| ResourceExhausted | 429 | 流量制限の返却に使う |
| Canceled | 499 | クライアント切断の慣用値 |
| Unimplemented | 501 | 未実装のRPC |
| Unavailable | 503 | 再試行の対象 |
| DeadlineExceeded | 504 | 上流のタイムアウト |
Internal・Unknown・DataLossは500へ落ちます。表で目を引くのはFailedPreconditionで、名前の似た412 Precondition Failedへは変換されず400になります。実装側のコメントでも意図的だと断られている挙動です。既存のREST仕様が409や422を返す約束になっているなら、WithErrorHandlerで応答本文と状態コードを差し替えます。
改行区切りJSONになるストリーミングと双方向が対象外の理由
ストリーミングRPCは、改行区切りのJSONストリームへ写されます。メッセージが1件ずつJSONになり、改行で区切られて流れる形です。HTTPのクライアントは、応答全体を1つのJSONとして読むのではなく、行単位で処理する必要があります。
一方で、真の双方向ストリーミングは対応予定なしと明記されています。HTTPとJSONの側にgRPCの双方向をそのまま写す方法が無いためで、実装の遅れではなく設計上の線引きです。クライアントから連続してメッセージを送る要件があるなら、そのRPCはgRPCのまま呼ばせることになります。ストリーミングの4方式と切断時の扱いは、gRPCストリーミングとは?4方式の使い分けと切断対策を実装者向けに解説で整理しています。
既存RESTクライアントを残したまま段階移行するための判断材料
実務でこの仕組みが選ばれるのは、既存資産との折り合いをつける場面です。旧URLで動いているクライアントを止められない、という前提から逆算します。
additional_bindingsで旧URLを残す段階移行の手順
HttpRuleにはadditional_bindingsがあり、1つのRPCへ複数のHTTPバインディングを与えられます。新しいパス体系をpatternに書き、旧来のパスをadditional_bindingsへ並べておけば、どちらのURLからも同じRPCへ届きます。ネストは1段までで、追加バインディング側にさらにadditional_bindingsは入れられません。
移行は、旧URLを追加バインディングとして温存したままアクセスログで呼び出し元を数え、ゼロになった時点でprotoから削る順序が現実的です。protoファイルへ手を入れられない事情がある場合は、アノテーションの代わりにgRPC API Configurationのyamlで対応関係を外出しする方法もあります。ただしREADMEはこれを部分的な対応と書いているため、生成結果を必ず確認してください。
PATCH要求のフィールドマスク自動変換と応答の整形で残る注意点
更新系では、PATCHリクエストがフィールドマスク付きのgRPCリクエストへ自動変換されます。本文に含まれたフィールドだけを更新対象としてマスクに詰めるため、部分更新のAPIを素直に書ける仕様です。手作業で組むと漏れやすい箇所なので、既定の挙動に乗る価値があります。
応答の見た目については、既定のマーシャリングがprotojsonである点を押さえます。フィールド名をprotoの表記のままにするか、キャメルケースへ寄せるかはマーシャラのオプション次第で、途中で変えると受け側が壊れます。WithMarshalerOptionで方針を固定し、値が空のフィールドを出力へ含めるかどうかもあわせて決めてください。応答にHTTPヘッダを足す場合はWithForwardResponseOption、応答の型を組み替える場合はWithForwardResponseRewriterが入口です。前段にAPIゲートウェイ製品を置く構成との役割分担は、APIゲートウェイとは?役割・機能とリバースプロキシ/サービスメッシュとの違い・導入判断を解説で切り分けてください。
gRPCからRESTを生やす五つの方式を並べた選定表と判断条件
同じ目的に届く方式は複数あり、実装言語と配置の制約で選べるものが絞られます。
| 方式 | 実行される場所 | 向く条件 |
|---|---|---|
| gRPC-Gateway | 生成したGoのプロキシ | サーバ言語を問わない |
| Envoyの変換フィルタ | プロキシ層 | 既にEnvoyが居る |
| ASP.NET Coreの変換 | アプリのプロセス内 | .NETで完結している |
| Connect | アプリのサーバ実装 | 新規に組み直せる |
| gRPC-Web | ブラウザとプロキシ | 型付きのまま呼びたい |
判断の分かれ目は二つです。ひとつはgRPCサーバの実装言語で、.NETで完結しているならASP.NET Coreのコード変換がアプリ内で動くぶん有利になります。マイクロソフトの解説でも、部品が減ることと、別サーバへgRPC呼び出しを飛ばさず処理できることが利点です。もうひとつはブラウザ側の要件で、JSONのRESTとして呼ばせたいのか、生成済みクライアントで型のまま呼ばせたいのかで方向が変わります。後者はgRPC-Webとは?ブラウザからgRPCを呼ぶ仕組みとプロキシ構成・採用判断を解説の領分です。サーバ実装ごと組み直せる新規案件なら、1つのサーバで両方を受けるConnect(connectrpc)とは?gRPC・RESTとの違いと使い方をわかりやすく解説も候補に入ります。
gRPC-Gatewayを見送る三つの条件と生成物を保つ運用体制
採用しないほうがよい形もはっきりしています。無理に通すと、変換層の例外処理が増えて保守の負担が残ります。
クライアント側ストリーミングが要る場合など採用を見送るべき三条件
第一に、クライアント側ストリーミングや双方向ストリーミングを含むRPCをHTTPで公開したい場合です。双方向は対応予定なしと明言されており、要件と正面からぶつかります。
第二に、既存のREST仕様が業務パラメータをHTTPヘッダで受けている場合です。ヘッダをメソッドパラメータへ束ねる機能は対応予定に無く、変換層の外側で自前の中継処理を書く羽目になります。既存クライアントの改修が許されないなら、別方式へ寄せる判断が要ります。
第三に、サーバが.NETで完結していて部品を増やしたくない場合です。ASP.NET CoreのJSONコード変換はアプリのプロセス内で動き、サーバの台数もホスティング費用も増えません。逆に、gRPCサーバがGo以外の複数言語に散っているなら、どのサーバにも同じ形で被せられるgRPC-Gatewayの汎用性が効きます。
生成物の版固定とCI再生成という運用を継続するための体制の要件
採用を決めたあと、品質を左右するのは生成物の扱いです。protoファイル、プラグインの版、生成コード、OpenAPI定義の四つが同じコミットで揃っている状態を、CIで機械的に守ってください。protoを変更したのにOpenAPIだけ古い、という食い違いは、外部連携先の実装に直接跳ね返ります。
あわせて、アノテーション設計のレビュー観点を決めます。パスの命名規則、bodyの指定方針、エラーコードの対応表、追加バインディングを残す期限の4点を先に文書化しておくと、RPCが増えたときの判断が揺れません。既存のgRPCサービスへREST入口を足す設計や連携方式の選定は、API開発・システム連携でご相談を承っています。
よくある質問
gRPC-GatewayとgRPC-Webは何が違うのですか?
目的が違います。gRPC-Gatewayはサーバ側にJSONのHTTP入口を生やす仕組みで、呼ぶ側は普通のREST APIとして扱う構成です。gRPC-Webはブラウザ向けに定められた別のワイヤ形式で、ブラウザ側に生成済みクライアントを置いて型のまま呼びます。JSONで呼ばせたいなら前者です。
gRPCサーバがGo以外でもgRPC-Gatewayは使えますか?
使えます。生成されるプロキシがGoのコードであるだけで、その先のgRPCサーバは任意の言語で構いません。この場合はプロキシを別プロセスとして立てる配置になり、ホップが1つ増えるため、遅延の許容範囲を先に確認してください。
protoファイルを変更せずにREST入口を足せますか?
gRPC API Configurationのyamlでマッピングを外出しする方法があり、protoへアノテーションを書かずに済みます。ただし公式READMEはこれを部分的な対応と位置づけているため、複雑なパステンプレートを使う場合は生成結果を確認したうえで採否を決めてください。
OpenAPI 3.1の出力は本番で使ってよいですか?
2026年9月時点では見送りをおすすめします。protoc-gen-openapiv3はv2.30.0で追加されたばかりで、READMEにアルファと明記され、出力形が今後変わりうると断られています。本番で配布する定義は、安定版のprotoc-gen-openapiv2から出力してください。
エラー応答の形式を既存のREST APIに合わせられますか?
既存形式への調整が可能です。WithErrorHandlerを渡せば、応答本文の構造もHTTPステータスも自分で決められます。ストリーミング中のエラーとルーティング段階の失敗は別のハンドラで受けるため、3つとも差し替えないと形式が混ざります。
関連記事
- gRPCとは?仕組み・RESTとの違い・UDP/TCPの扱いを実装例つきで解説:土台となるgRPC本体の仕組み
- Protocol Buffersとは?protoファイルの書き方と採用判断を実装者向けに解説:アノテーションを書くスキーマ側の作法
- gRPCストリーミングとは?4方式の使い分けと切断対策を実装者向けに解説:変換できるストリーミングの範囲
- gRPC-Webとは?ブラウザからgRPCを呼ぶ仕組みとプロキシ構成・採用判断を解説:ブラウザから型のまま呼ぶ場合の方式
- Connect(connectrpc)とは?gRPC・RESTとの違いと使い方をわかりやすく解説:一体型サーバという代替
- APIゲートウェイとは?役割・機能とリバースプロキシ/サービスメッシュとの違い・導入判断を解説:前段に置く部品との役割分担