ブラウザから gRPC サービスを呼び出そうとすると、すぐに制約に当たります。ブラウザは HTTP トレーラーを制御できないため、生の gRPC を直接扱えません。その結果、Envoy のようなプロキシを追加してトラフィックを変換する構成になりがちです。ConnectRPC を使うと、Protobuf ベースの API をブラウザから直接呼び出せる形で公開しつつ、curl でも確認でき、既存の gRPC エコシステムとも相互運用できます。
この記事では、ConnectRPC の基本、Connect プロトコルが解決する課題、gRPC / gRPC-Web / REST との違い、HTTP 上でのリクエスト形式を実装目線で整理します。最後に、Connect と gRPC エンドポイントを curl や Apidog でテストし、CI に組み込む流れも紹介します。
ConnectRPCとは
ConnectRPC は、Protocol Buffers からブラウザ互換かつ gRPC 互換の HTTP API を構築するためのライブラリ群です。
基本的な開発フローは次のとおりです。
-
.protoファイルでサービスとメッセージを定義する -
bufでコードを生成する - 生成されたインターフェースに対してハンドラを実装する
- 通常の HTTP サーバーに Connect ハンドラをマウントする
- ブラウザ、Node.js、Go、
curl、gRPC クライアントなどから呼び出す
ConnectRPC は、Protobuf 用の buf ビルドツールを開発した Buf によって作成され、現在は Cloud Native Computing Foundation(CNCF)のサンドボックスプロジェクトです。既に buf と Protobuf を使っているチームであれば、スキーマファーストのワークフローに自然に組み込めます。
ConnectRPC サーバーは、同じサービス定義から次の 3 つのプロトコルを扱えます。
- gRPC: ストリーミングとトレーラーを含む完全な互換性
- gRPC-Web: ブラウザ向け gRPC-Web を直接サポート
- Connect: 標準 HTTP 上で扱いやすい独自プロトコル
つまり、単一のバックエンドで次のようなクライアントをまとめて受けられます。
- 既存の gRPC クライアント
- ブラウザ上の TypeScript クライアント
-
curlや通常の HTTP クライアント - gRPC-Web クライアント
多くの場合、クライアント側の設定で使用プロトコルを選ぶだけで、サービス実装自体を書き換える必要はありません。
Connectが解決する問題
gRPC は高速で型安全ですが、ブラウザとの相性には制約があります。主な理由は、gRPC が HTTP/2 と HTTP トレーラーに依存してステータス情報を伝えるためです。
ブラウザの JavaScript からは HTTP トレーラーを自由に扱えません。そのため、ブラウザから gRPC バックエンドを呼ぶ場合は、一般的に次のような構成になります。
Browser
-> gRPC-Web
-> Envoyなどのプロキシ
-> gRPC backend
この構成は動作しますが、運用上のコストが増えます。
- プロキシのデプロイが必要
- ルーティング設定が増える
- 障害点が増える
- デバッグ対象のホップが増える
- ローカル開発環境が複雑になる
Connect は、ブラウザが直接扱える標準 HTTP ベースのプロトコルを提供します。
Browser
-> Connect over HTTP
-> ConnectRPC server
さらに同じサーバーが gRPC も扱えるため、バックエンド間通信では gRPC、フロントエンドからは Connect、手元の確認では curl という使い分けができます。
Connect vs gRPC vs gRPC-Web vs REST
それぞれの使い分けを整理すると、Connect の位置づけが分かりやすくなります。
| 方式 | 主な特徴 | ブラウザ対応 | 型安全性 | デバッグしやすさ |
|---|---|---|---|---|
| gRPC | HTTP/2、Protobuf、トレーラー、ストリーミング | 直接は扱いにくい | 高い | 低め |
| gRPC-Web | ブラウザ向け gRPC | 対応可能 | 高い | 中程度 |
| Connect | 標準 HTTP、JSON / Protobuf、gRPC 互換 | 直接呼び出しやすい | 高い | 高い |
| REST | リソース指向、JSON が一般的 | 高い | 実装次第 | 高い |
gRPC は、HTTP/2 上で Protobuf とバイナリフレーミングを使います。効率的で、単項、サーバーストリーミング、クライアントストリーミング、双方向ストリーミングをサポートします。一方で、ブラウザやシェルから直接扱うには不向きです。トランスポート層については、gRPCとHTTP/2がどのように連携するかも参考になります。
gRPC-Web は、gRPC をブラウザから使うためのバリアントです。Protobuf を維持しつつ、通常は実際の gRPC バックエンドとの間にプロキシを置きます。詳しくは gRPC-Webとは何か を参照してください。
Connect は、Protobuf スキーマと gRPC スタイルのメソッドモデルを維持しながら、HTTP/1.1、HTTP/2、HTTP/3 上で扱いやすく設計されています。単項呼び出しでは、gRPC のバイナリフレーミングなしに JSON または Protobuf のボディを送信でき、HTTP ステータスコードも通常どおり使えます。
REST は最も広く使われる API スタイルです。ただし、Protobuf が提供する厳密な型付けやコード生成は標準ではありません。REST と gRPC の違いは gRPC vs REST で詳しく説明しています。
要するに、Connect は gRPC の型安全性と Protobuf ベースのコード生成を維持しながら、REST のように扱いやすい HTTP インターフェースを提供することを目指しています。ProtobufとgRPCのメソッドモデルを使いながら、ブラウザやシェルが扱いにくい部分を取り除いています。
HTTP上でのConnectリクエスト
Connect の単項呼び出しは、通常の HTTP POST として送信できます。
URL パスは Protobuf スキーマから決まります。
/<package>.<Service>/<Method>
例えば、次のようなサービスがあるとします。
syntax = "proto3";
package greet.v1;
service GreetService {
rpc Greet(GreetRequest) returns (GreetResponse);
}
message GreetRequest {
string name = 1;
}
message GreetResponse {
string greeting = 1;
}
この場合、Greet メソッドのエンドポイントは次のようになります。
/greet.v1.GreetService/Greet
JSON で呼び出す場合は、Content-Type: application/json を指定します。
curl \
--header "Content-Type: application/json" \
--data '{"name": "Jane"}' \
http://localhost:8080/greet.v1.GreetService/Greet
レスポンスも通常の JSON として確認できます。
{"greeting": "Hello, Jane!"}
この形式であれば、次のようなツールでそのまま検証できます。
curl- ブラウザの DevTools
- HTTP クライアント
- Apidog
- CI 上の HTTP テスト
Connect の単項リクエストでは、主に次の 2 種類のコンテンツタイプを使います。
-
application/json: 人間が読める JSON ペイロード -
application/proto: コンパクトなバイナリ Protobuf ペイロード
開発中は JSON を使うと確認しやすく、本番やサイズを重視する場面では Protobuf を選べます。
バイナリ Protobuf を送る場合は、ヘッダーを切り替えてエンコード済みのボディを渡します。
curl \
--header "Content-Type: application/proto" \
--data-binary @request.bin \
http://localhost:8080/greet.v1.GreetService/Greet
ストリーミング呼び出しでは、単項呼び出しとは異なるコンテンツタイプを使います。
application/connect+protoapplication/connect+json
ストリーミング時は、各メッセージがエンベロープでラップされます。エンベロープには次の要素が含まれます。
1 byte : flags
4 bytes : message length, big-endian
N bytes : message payload
一方、単項呼び出しではこのエンベロープが不要です。そのため、通常の HTTP リクエストとして非常に扱いやすくなります。
JSON と Protobuf の使い分けについては、Protobuf vs JSON でサイズと可読性のトレードオフを確認できます。
言語サポートとbufツール
ConnectRPC は複数の言語で実装を提供しています。
- Go: 安定、本番利用向け
- TypeScript: ブラウザおよび Node.js 向け、安定
- Swift: Apple プラットフォーム向け
- Kotlin: JVM および Android 向け
- Python: ベータ版
例えば、Go でバックエンドを実装し、TypeScript でフロントエンドクライアントを生成する場合でも、同じ .proto ファイルを契約として共有できます。
コード生成には buf ツールチェーンを使います。Go プロジェクトでは、一般的に次のプラグインを使います。
-
protoc-gen-go: Protobuf メッセージ用の Go コードを生成 -
protoc-gen-connect-go: Connect のサーバーおよびクライアントコードを生成
buf.gen.yaml の例は次のようになります。
version: v2
plugins:
- remote: buf.build/protocolbuffers/go
out: gen
opt:
- paths=source_relative
- remote: buf.build/connectrpc/go
out: gen
opt:
- paths=source_relative
生成は 1 コマンドです。
buf generate
生成後は、型付きのメッセージ構造体、サービスインターフェース、HTTP ハンドラ登録用コードを利用できます。
Go では、Connect ハンドラは標準ライブラリの net/http と組み合わせて使えます。専用のサーバーランタイムを導入する必要はありません。
概念的には次のようにマウントします。
mux := http.NewServeMux()
path, handler := greetv1connect.NewGreetServiceHandler(
&GreetServer{},
)
mux.Handle(path, handler)
http.ListenAndServe(":8080", mux)
これにより、既存の HTTP ルートと同じサーバー上に Connect エンドポイントを追加できます。
ConnectおよびgRPCエンドポイントのテストとデバッグ
Connect の単項呼び出しは JSON ボディを持つ HTTP POST として扱えるため、REST API と同じ感覚でテストできます。
まずは curl で疎通を確認します。
curl \
--header "Content-Type: application/json" \
--data '{"name": "Jane"}' \
http://localhost:8080/greet.v1.GreetService/Greet
期待するレスポンスは次のような JSON です。
{"greeting": "Hello, Jane!"}
HTTP ステータスコードも通常どおり確認できます。
curl \
-i \
--header "Content-Type: application/json" \
--data '{"name": "Jane"}' \
http://localhost:8080/greet.v1.GreetService/Greet
Apidog で Connect の単項リクエストを確認する場合は、通常の HTTP リクエストとして設定します。
- メソッドを
POSTにする - URL に
http://localhost:8080/greet.v1.GreetService/Greetを指定する - ヘッダーに
Content-Type: application/jsonを追加する - Body にリクエスト JSON を入力する
{"name": "Jane"}
実行すると、JSON レスポンスと HTTP ステータスコードを確認できます。Connect 専用クライアントでなくても、Connect が公開している HTTP インターフェースをそのまま扱えるためです。
HTTP メソッドの基礎を確認したい場合は、HTTPメソッドとは何かも参考になります。
Connect サーバーは gRPC も話せるため、同じサービスの gRPC 側もテストできます。Apidog は gRPC エンドポイントをサポートしているため、次の流れで確認できます。
-
.protoファイルをインポートする - サービスとメソッドを選択する
- 型付きリクエストを入力する
- gRPC レスポンスを確認する
これにより、HTTP フレンドリーな Connect プロトコルとネイティブ gRPC プロトコルの両方を同じツールで検証できます。gRPC のテストワークフローについては、gRPC APIを効率的にテストする方法で詳しく説明しています。
CIでConnectとgRPCの回帰テストを実行する
手元でリクエストが期待どおりに動作したら、保存したテストシナリオを CI で実行します。
Apidog CLI は Node.js でインストールできます。
npm install -g apidog-cli
保存済みのシナリオまたはスイートを実行する例です。
apidog run \
--access-token "$APIDOG_ACCESS_TOKEN" \
-t <scenarioOrSuiteId> \
-e <environmentId> \
-r cli,html,junit
CI に組み込む場合は、次の値を環境変数やシークレットとして管理します。
APIDOG_ACCESS_TOKEN- シナリオまたはスイート ID
- 環境 ID
CLI はヘッドレスで実行できるため、Node.js を利用できる CI ステップに追加できます。アドホックなリクエストではなく、保存済みのテストシナリオやスイートを実行するため、Connect および gRPC エンドポイントの回帰テストに適しています。
詳しい手順は、コマンドラインからAPIをテストするためのApidog CLIチュートリアルを参照してください。
よくある質問
ConnectRPCはgRPCと同じですか?
いいえ。ConnectRPC は、gRPC、gRPC-Web、Connect プロトコルを実装するフレームワークです。Connect サーバーは gRPC クライアントおよびサーバーと相互運用できますが、Connect プロトコル自体はブラウザやシェルから直接呼び出しやすい HTTP ベースの設計です。
ブラウザに到達するためにEnvoyのようなプロキシはまだ必要ですか?
Connect プロトコルを使う場合は不要です。Connect は標準 HTTP 上で動作するため、ブラウザは翻訳プロキシなしで Connect サーバーを呼び出せます。Envoy のようなプロキシが必要になるのは、ブラウザトラフィックをネイティブ gRPC のみを話すバックエンドに橋渡しする場合です。
ConnectRPCはどの言語をサポートしていますか?
Go と TypeScript は安定した本番環境対応の実装です。Swift、Kotlin、Python も利用可能で、Python はベータ版です。いずれも同じ Protobuf スキーマからコード生成できます。
Connectとbufの関係は何ですか?
ConnectRPC は Buf によって作成され、コード生成には buf ツールチェーンを使います。.proto ファイルからハンドラとクライアントを生成するために、protoc-gen-connect-go のようなプラグインとともに buf generate を実行します。
通常のAPIクライアントでConnectエンドポイントをテストできますか?
はい。Connect の単項呼び出しは、JSON または Protobuf ボディと実際の HTTP ステータスコードを持つ HTTP POST リクエストです。curl や Apidog を含む通常の HTTP クライアントで送信できます。Apidog では .proto ファイルをインポートして、同じサーバーの gRPC 側も呼び出せます。
Top comments (0)