Connect(Connect RPC、connectrpc)は、Protocol Buffersで定義したAPIを、ブラウザからもgRPCクライアントからも呼べるHTTP APIとして実装するためのライブラリ群です。開発元はBufで、公式サイトはconnectrpc.comです。英単語のconnectと紛らわしいものの、ここで扱うのはこのRPC用のConnectだけです。1つのサーバーがConnectプロトコル・gRPC・gRPC-Webの3方式を同時に受け付けるので、ブラウザは普通のHTTP POST、社内のサービスはgRPCという使い分けを同じエンドポイントで実現できます。2026年10月にGo実装のconnect-goがv2になり書き方が大きく変わったため、本記事はv2で実際に動かした結果をもとに、定義・gRPCやRESTとの違い・使い方を整理します。
まとめ:Connect RPCの要点
- Connect=Buf製のRPCライブラリ群。2024年4月13日にCNCFのSandboxプロジェクトとして受け入れられた。
- 1サーバーで3プロトコル(Connect/gRPC/gRPC-Web)。ConnectプロトコルのunaryはJSONまたはバイナリProtobufを使い、通常はPOSTで送る。JSONならcurlやブラウザのfetchでも呼べ、副作用のないメソッドはGETにも対応する。
- gRPCとの違い:ブラウザ向けのプロキシ(Envoy等)が要らず、HTTP/1.1でも動く。ただしネイティブgRPCクライアントを受けるにはHTTP/2(平文ならh2c)の設定が必要。
- connect-go v2(2026-10-07公開)で
connect.Request[T]のラッパーが廃止され、メソッドは素のメッセージを受け取って返す形になった。v1も無期限でサポートが続く。 - TypeScriptはconnect-es 2.2.0とconnect-query 2.3.1、Pythonは
connectrpc0.12.1(ベータ)。
Connect RPCの定義と生まれた背景
gRPCをブラウザとHTTPツールから扱えるようにする設計
RPC(Remote Procedure Call)は、別のサーバーにある関数をローカルの関数のように呼ぶ通信方式で、代表がgRPCです。gRPCそのものの仕組みはgRPCとは?定義・仕組み・RESTとの違いをまとめて解説で整理しています。gRPCはHTTPトレーラーを使うため、トレーラーを扱えないブラウザからは直接呼べません。従来はgRPC-Webとは?ブラウザからgRPCを呼ぶ仕組みとプロキシ構成・採用判断を解説のように変換プロキシを挟む必要がありました。Connectは、Connectプロトコル仕様で「HTTPトレーラーを一切使わない」と定めることで、この制約を外しています。
プロジェクトは2024年4月13日にCNCFのSandboxとして受け入れられています(CNCFのConnect RPCページ)。Sandboxは、CNCFが初期段階のプロジェクトを受け入れる入口の区分で、Incubating・Graduatedより前の段階にあたります。
1つのハンドラがConnect・gRPC・gRPC-Webを受け付ける仕組み
サーバーは届いたリクエストのContent-Typeで方式を判別します。unaryの場合、application/jsonやapplication/protoならConnect、application/grpcならgRPC、application/grpc-webならgRPC-Webとして処理します(Connectのストリーミングはapplication/connect+jsonかapplication/connect+proto)。後述の実測でも、対応外のContent-Typeを送ると415が返り、Accept-Postヘッダーに受け付ける8種類が並びました。生成したハンドラを1回登録するだけで、3方式に追加の実装は要りません。
Connect・gRPC・RESTの違い
3つともクライアントとサーバーをつなぐ方式ですが、前提とする通信環境が異なります。
| 観点 | Connect | gRPC | REST(JSON) |
|---|---|---|---|
| スキーマ | Protocol Buffers | Protocol Buffers | OpenAPI等(任意) |
| HTTPの版 | HTTP/1.1・HTTP/2 | HTTP/2 | HTTP/1.1・HTTP/2 |
| ブラウザから直接 | 可 | 不可(プロキシ必須) | 可 |
| ペイロード | JSON/Protobuf | Protobuf(JSONも可) | JSON中心 |
| URL | /パッケージ.サービス/メソッド | 同左 | リソース単位で自由 |
| エラー | gRPCと同じ16コード | 16コード | HTTPステータス |
| 双方向ストリーミング | HTTP/2で可 | 可 | 不可 |
ConnectはgRPCの置き換えではなく、gRPCと同じスキーマ・同じエラーコードを使いながらHTTPの道具で扱えるようにした方式です。URLがスキーマから決まりRESTのように自由に設計できない点は、公開APIとして見せたいときの制約になります(後述のOpenAPIの章)。型安全なAPIをTypeScriptだけで完結させたい場合は、独立したProtobuf定義を必要としないtRPCとは?REST・GraphQL・gRPCとの違いと型安全なAPI実装をv11の実コードで解説も比較対象になります。
Connectプロトコルのcurlによる通信検証
ここからは、後述のconnect-go v2で立てたGreetサービス(localhost:8080)に対して、2026年10月17日に実行した結果です。
unary呼び出しのJSON POST形式
パスは/<パッケージ>.<サービス>/<メソッド>で、ボディは素のJSONです。gRPCのような5バイトのフレーム(長さの前置き)は付きません。
$ curl -i --http1.1 -H 'Content-Type: application/json' \
-d '{"name":"Jane"}' http://localhost:8080/greet.v1.GreetService/Greet
HTTP/1.1 200 OK
Accept-Encoding: gzip
Content-Type: application/json
Content-Length: 26
{"greeting":"Hello, Jane"}
仕様ではクライアントがConnect-Protocol-Version: 1ヘッダーを送ることを推奨し、サーバーやプロキシはこのヘッダーが無いリクエストを400で拒否してもよいとしています。connect-goの既定は拒否しないため、上の例はヘッダー無しでも通りました。本番の検証では付けておくと、ヘッダーを要求する構成でもそのまま使えます。
副作用のないRPCのGET呼び出し条件
protoのメソッドにoption idempotency_level = NO_SIDE_EFFECTS;を付けると、GETでも呼べるようになります。メッセージはURLのクエリに入ります。ただし、GETにするだけでCDNやブラウザに必ずキャッシュされるわけではありません。保存期間や共有可否に合わせて、サーバー側でCache-Controlなどのレスポンスヘッダーを設定します。
$ curl -i --http1.1 'http://localhost:8080/greet.v1.GreetService/Greet?connect=v1&encoding=json&message=%7B%22name%22%3A%22Jane%22%7D'
HTTP/1.1 200 OK
Content-Type: application/json
Vary: Accept-Encoding
{"greeting":"Hello, Jane"}
クエリを付けずにGETを送ると、415 Unsupported Media Typeが返りました。TypeScriptのクライアントでは、トランスポートにuseHttpGet: trueを渡すとこの形式を使います。
unaryのエラーコードとHTTPステータスの対応
Connectプロトコルのunaryでは、エラーに対応するHTTPステータスとJSONのcodeが返り、messageは省略される場合があります。ストリーミングでは、HTTPステータスは200のまま、終端メッセージにエラーが格納されます。
$ curl -i --http1.1 -H 'Content-Type: application/json' -d '{}' \
http://localhost:8080/greet.v1.GreetService/Greet
HTTP/1.1 400 Bad Request
Content-Type: application/json
{"code":"invalid_argument","message":"name is required"}
コードとHTTPステータスの対応は、プロトコル仕様の表で決まっています。主なものは次のとおりです。
| Connectのコード | HTTPステータス |
|---|---|
| invalid_argument/failed_precondition/out_of_range | 400 |
| unauthenticated | 401 |
| permission_denied | 403 |
| not_found | 404 |
| already_exists/aborted | 409 |
| resource_exhausted | 429 |
| canceled | 499 |
| unknown/internal/data_loss | 500 |
| unimplemented | 501 |
| unavailable | 503 |
| deadline_exceeded | 504 |
HTTPステータスだけでは400が3つのコードに重なるため、クライアントはステータスでなくcodeで分岐します。HTTPステータスをそのまま使わない理由について、公式FAQは「gRPCとHTTPステータスの対応が一対一でなく、gRPCとの互換を保つには同じコード体系を採るしかない」と説明しています。gRPC側の16コードの意味とリトライ可否はgRPCのステータスコードとは?16種の意味・HTTP対応表・リトライ可否を解説にまとめています。
ストリーミングでHTTP/2が要る範囲
Connectプロトコルはunary・クライアントストリーミング・サーバーストリーミング・双方向ストリーミングの4種に対応します。仕様上、HTTP/2が必須なのは双方向ストリーミングだけで、ほかの3種はHTTP/1.1でも動きます。ブラウザ向けの@connectrpc/connect-web(2.2.0)はさらに狭く、unaryとサーバーストリーミングしか呼べません(connect-transport.tsの実装)。チャットのような用途は、送信をunary、受信をサーバーストリーミングに分ければ組めます。4方式の使い分けはgRPCストリーミングとは?4方式の使い分けと切断対策を実装者向けに解説が参考になります。
connect-go v2でサーバーを動かす手順
connect-goは2026年10月7日にv2.0.0、10月14日にv2.0.1が公開されました(v2.0.0のリリースノート)。モジュールパスはconnectrpc.com/connect/v2で、go.modが要求するGoは1.26.0以上です。公式のGetting startedもv2の書き方に切り替わっています。以下はGo 1.27.2で実行した手順です。
BufとGo用プラグインの導入・コード生成
空の作業ディレクトリでgo mod init exampleを実行します。Goの生成プラグインを呼び出せるよう、インストール先のGOBIN(未設定ならGOPATHのbinディレクトリ)をPATHに追加してください。コード生成プラグインもv2のモジュールから入れます。v1のプラグインのままだと、v2の書き方のコードは出ません。
go install google.golang.org/protobuf/cmd/[email protected]
go install connectrpc.com/connect/v2/cmd/[email protected]
# buf CLIはnpmでも入る(今回は1.74.0)
npm i -D @bufbuild/buf
スキーマはproto/greet/v1/greet.protoに置きます。GETで呼べるようNO_SIDE_EFFECTSを付けています。
syntax = "proto3";
package greet.v1;
option go_package = "example/gen/greet/v1;greetv1";
message GreetRequest {
string name = 1;
}
message GreetResponse {
string greeting = 1;
}
service GreetService {
rpc Greet(GreetRequest) returns (GreetResponse) {
option idempotency_level = NO_SIDE_EFFECTS;
}
}
buf.yamlとbuf.gen.yamlを置き、npx buf generateを実行するとgen/greet/v1/greet.pb.goとgen/greet/v1/greetv1connect/greet.connect.goができます。
# buf.yaml
version: v2
modules:
- path: proto
# buf.gen.yaml
version: v2
plugins:
- local: protoc-gen-go
out: gen
opt: paths=source_relative
- local: protoc-gen-connect-go
out: gen
opt: paths=source_relative
protoファイルの書き方とフィールド番号の運用はProtocol Buffersとは?protoファイルの書き方と採用判断を実装者向けに解説を参照してください。
v2のサーバー実装とh2cの設定
v2では、メソッドは生成されたメッセージ型を直接受け取って返します。サービスをconnect.Serverに登録し、connecthttp.Mountでhttp.ServeMuxへ載せます。次のコードを作業ディレクトリ直下のmain.goに保存し、go get connectrpc.com/connect/[email protected]、go mod tidy、go run .の順に実行します。以降のコマンドは、サーバーを起動したまま別のターミナルで実行します。
package main
import (
"context"
"errors"
"log"
"net/http"
"connectrpc.com/connect/v2"
"connectrpc.com/connect/v2/connecthttp"
greetv1 "example/gen/greet/v1"
"example/gen/greet/v1/greetv1connect"
)
type greetServer struct {
greetv1connect.UnimplementedGreetServiceHandler
}
func (greetServer) Greet(ctx context.Context, req *greetv1.GreetRequest) (*greetv1.GreetResponse, error) {
switch req.Name {
case "":
return nil, connect.NewError(connect.CodeInvalidArgument, "name is required")
case "db":
// connect.Error以外のerrorは、メッセージがクライアントへ送られない
return nil, errors.New("dial tcp 10.0.0.5:5432: connection refused")
}
return &greetv1.GreetResponse{Greeting: "Hello, " + req.Name}, nil
}
func main() {
server := connect.NewServer()
greetv1connect.RegisterGreetServiceHandler(server, greetServer{})
mux := http.NewServeMux()
connecthttp.Mount(mux, server)
protocols := new(http.Protocols)
protocols.SetHTTP1(true)
protocols.SetUnencryptedHTTP2(true) // TLSなしでgRPCクライアントを受けるためのh2c
srv := &http.Server{Addr: "localhost:8080", Handler: mux, Protocols: protocols}
log.Fatal(srv.ListenAndServe())
}
http.ProtocolsはGo 1.24で入った標準ライブラリの機能で、以前のようにgolang.org/x/net/http2/h2cでハンドラを包む必要はなくなりました。公式のDeployment & h2cの手順も同じ書き方です。
同一URLへのgRPC・gRPC-Web接続検証
同じサーバーに、buf curlで方式を切り替えて送りました。
$ npx buf curl --schema . --protocol grpc --http2-prior-knowledge \
-d '{"name":"Jane"}' http://localhost:8080/greet.v1.GreetService/Greet
{
"greeting": "Hello, Jane"
}
$ npx buf curl --schema . --protocol grpcweb \
-d '{"name":"Jane"}' http://localhost:8080/greet.v1.GreetService/Greet
{
"greeting": "Hello, Jane"
}
次にSetUnencryptedHTTP2(true)の行を外し、待受アドレスもlocalhost:8081へ変更して起動したサーバーへ送ると、ConnectとgRPC-Webは成功し、gRPCだけが失敗しました。
$ npx buf curl --schema . --protocol grpc --http2-prior-knowledge \
-d '{"name":"Jane"}' http://localhost:8081/greet.v1.GreetService/Greet
{
"code": "unavailable",
"message": "http2: failed reading the frame payload: http2: frame too large, note that the frame header looked like an HTTP/1.1 header"
}
ローカルではConnectで動いたのに、gRPCクライアントからだけunavailableになる場合は、まずHTTP/2の設定を疑ってください。gRPCクライアント側をGoで書く手順はgRPC Goの実装|protoc-gen-go-grpcの生成手順とNewClient移行を解説にあります。
v1からv2で変わった書き方と移行ツール
v2で変わったのはGoのAPIで、通信の中身(ワイヤプロトコル)とgRPC互換は変わっていません。リリースノートとv2ガイドから、書き直しが要る箇所を抜き出しました。
| 項目 | v1 | v2 |
|---|---|---|
| モジュール | connectrpc.com/connect | connectrpc.com/connect/v2 |
| メソッドの引数 | *connect.Request[T] | *T(素のメッセージ) |
| 戻り値 | *connect.Response[T] | *T |
| サーバー登録 | NewXxxServiceHandler | RegisterXxxServiceHandler+Mount |
| NewErrorの第2引数 | error | string |
| テスト | httptestで実サーバー | connectinprocessで通信なし |
運用で効くのはエラーの扱いです。v2では、自分で作った*connect.Errorだけがメッセージを送ります。上のコードでnameにdbを渡すとクライアントには{"code":"unknown"}とHTTP 500だけが届き、DBの接続先を含むエラー文は漏れませんでした。v1の感覚でerrors.Newをそのまま返している箇所は、v2移行後に「原因が何も見えないunknown」へ変わるので、connect.NewError(...).WithCause(err)で書き直しておく必要があります。
移行はconnect-go-v2-migrateがbuf.gen.yamlとGoのコードの大半を書き換えます。v1はv1ブランチで無期限にバグ修正とセキュリティ修正が続き(10月14日にもv1.21.1が出ています)、モジュールパスが別なので1つのモジュールで両方を使いながらサービス単位で移せます。急いで移す必要はありませんが、新規のコードはv2で書くのが妥当です。
go install connectrpc.com/connect/v2/cmd/connect-go-v2-migrate@latest
connect-go-v2-migrate -w
npx buf generate
connect-go-v2-migrate -w
go mod tidy
connect-esによるブラウザ・Node.jsクライアント実装
TypeScript実装はconnect-es(@connectrpc/connect 2.2.0)です。v2からはコード生成がprotoc-gen-esの1本になり、生成された*_pb.tsにサービス定義も入ります。上のGoサーバーと同じ作業ディレクトリで、Node.js v26.5.0から呼べることを確認しました。
npm install @connectrpc/connect @connectrpc/connect-web @bufbuild/protobuf
npm install -D @bufbuild/protoc-gen-es
先ほどのbuf.gen.yamlのpluginsに次の3行を足し、もう一度npx buf generateを実行するとgen/greet/v1/greet_pb.tsができます。
- local: protoc-gen-es
out: gen
opt: target=ts
npm pkg set type=moduleでESモジュールにしたうえで、次のコードをclient.tsとして保存し、node client.tsで実行します。Node.js v26は型注釈を取り除いてTypeScriptをそのまま実行できます。ブラウザから別オリジンのGoサーバーへ接続する場合は、これに加えてサーバー側でCORSを設定し、接続元のオリジン、GET・POST、Content-TypeやConnect-Protocol-Versionなど使用するリクエストヘッダーを許可します。独自のレスポンスヘッダーを読む場合は、その公開設定も必要です。今回の確認はNode.jsからの呼び出しまでで、ブラウザからの接続は試していません。
import { createClient, ConnectError } from "@connectrpc/connect";
import { createConnectTransport } from "@connectrpc/connect-web";
import { GreetService } from "./gen/greet/v1/greet_pb.ts";
const transport = createConnectTransport({ baseUrl: "http://localhost:8080", useHttpGet: true });
const client = createClient(GreetService, transport);
const res = await client.greet({ name: "Jane" });
console.log(res.greeting); // Hello, Jane
try {
await client.greet({ name: "" });
} catch (e) {
const err = ConnectError.from(e);
console.log(err.code, err.rawMessage); // 3 name is required
}
err.codeは列挙値の数値で、3がInvalidArgumentです。Reactでデータ取得に使うconnect-query(2.3.1)はTanStack Queryの拡張で、TransportProviderにトランスポートを渡し、useQuery(GreetService.method.greet, { name: "Jane" })のように生成済みのメソッド定義を渡して使います。キャッシュキーはサービス名・メソッド名・入力・トランスポートなどから自動生成されます。トランスポートもキーに関わるため、Reactの再レンダーごとに作り直さず、参照を安定させます。Remix(React Router v8)のloaderから呼ぶ構成はgRPC×Connect-ESをRemix(React Router v8)で使う実装|v2の生成手順とloader連携で扱っています。
Python版connect-py(connectrpc)の現状
Pythonには公式のconnect-pyがあり、PyPIのパッケージ名はconnectrpcです。2026年9月4日公開の0.12.1が最新で、PyPIの分類は「Development Status :: 4 – Beta」、要求するPythonは3.10以上です(PyPIのconnectrpc)。
uv add connectrpc
uv add --dev protoc-gen-py protoc-gen-connectrpc
サーバーはWSGIとASGIの両方で動き、生成されたXxxServiceASGIApplicationをuvicorn等で起動します。既存のWSGI・ASGIアプリと同じアプリの中に並べて置けるのが特徴です。ライブラリはConnect・gRPC・gRPC-Webの3方式に対応しますが、gRPCを受け付けるには、pyvoyなどHTTP/2トレーラーを扱えるサーバーが必要です。1.0前のため、版を固定し、更新時はリリースノートを確認してください。名前の似たconnecpyなど非公式の実装もあるので、パッケージ名を取り違えないよう注意が必要です。Pythonで素のgRPCを使う場合との違いはgRPC Pythonの実装|スタブ生成からasync・FastAPI併存まで解説と比べると分かりやすくなります。
対応言語と実装の成熟度
connectrpcのGitHub組織にある公式実装を、各リリースとレジストリで確認しました(2026年10月17日時点)。
| 言語 | 実装 | 最新版 | 役割 |
|---|---|---|---|
| Go | connect-go | v2.0.1(v1.21.1) | サーバー・クライアント |
| TypeScript | connect-es | 2.2.0 | ブラウザ・Node.js |
| Swift | connect-swift | 1.2.3 | クライアント |
| Kotlin | connect-kotlin | 0.9.0 | クライアント |
| Dart | connect-dart | 2.0.0 | クライアント |
| Python | connect-py | 0.12.1(ベータ) | サーバー・クライアント |
| Rust | connect-rust | 0.9.1(1.0未満) | サーバー・クライアント |
Kotlinは1.0に達していないので、モバイルで採用するなら版の固定が前提です。JavaやC#の公式実装はありませんが、Connectのサーバーは標準のgRPCも話すので、これらの言語からは既存のgRPCクライアントで接続できます。逆に、既存のgRPCサーバーをConnectプロトコルで呼びたい場合は、Envoy 1.26以降に入っているConnect-gRPCブリッジで変換できます(公式FAQ)。
OpenAPI文書の生成とREST風URLへの変換
公式FAQの立場は「Connectの仕様と文書はProtobufそのもので、OpenAPIはRPCには当てはまらない」です。URLもスキーマの完全修飾名で固定され、変更するオプションはありません。変えるとgRPC・gRPC-Webとの互換が崩れるためです。
それでもOpenAPIの文書が欲しい場合は、第三者のプラグインprotoc-gen-connect-openapi(2026年9月30日公開のv0.28.0)でConnectプロトコルに合わせたOpenAPI 3.1を生成できます。GET /users/{id}のようなREST風のURLで公開したいなら、gRPC Transcodingを使うvanguard-goか、gRPC-Gatewayとは?protoからRESTを生やす仕組みと生成構成・移行判断を解説の構成を選ぶことになります。
Connectを選ぶべき場面・避けるべき場面
Connectが最も効くのは、Protocol Buffersでスキーマを持ち、そのAPIをブラウザから直接呼びたい場面です。次の条件に当てはまるなら採用を勧めます。
- gRPC-Web用のEnvoyを運用したくない:サーバー1つでブラウザとgRPCクライアントの両方を受けられる。
- 既存のgRPCサービスがある:同じprotoとエラーコードのまま、ブラウザ向けの入口だけをConnectで足せる。
- CDNでキャッシュしたい参照系がある:
NO_SIDE_EFFECTSのメソッドは、クライアントでもGETを有効にするとGETで呼べる。Cache-Controlなどを適切に設定すれば、通常のHTTPキャッシュを利用できる。
反対に、次の場合は主軸に据えない方がよいです。ブラウザからクライアントストリーミングや双方向ストリーミングをしたいなら、connect-webでは呼べないのでWebSocketなど別の手段が要ります。社内にProtocol Buffersの運用基盤がなく、外部向けのAPIをJSONとOpenAPIで公開したいなら、RESTの方が学習と運用の負担は小さく済みます。gRPCの通信効率やxDSによる負荷分散など、gRPC固有のエコシステムが目的なら素のgRPCで十分です。Connectで作るAPIの設計や既存システムとの連携をまとめて相談したい場合は、API開発・システム連携の支援でも対応しています。
よくある質問
Connectの読み方は?
「コネクト」と読みます。英単語のconnectと区別するため、ConnectRPC、connectrpcとも表記されます。パッケージ名やドメインconnectrpc.comはこの綴りです。
connect-rpcとConnect RPCは同じものですか?
同じものです。公式FAQによると、もとの名前はConnectで、検索で紛らわしいためConnectRPCという呼称に移ったとされています。GitHubの組織名とドメインがconnectrpc、npmのスコープが@connectrpcです。かつて@bufbuild/connectやgithub.com/bufbuild/connect-goとして配布されていたものが、この名前へ移りました。
gRPCとConnectはどちらを選ぶべきですか?
ブラウザから直接呼ぶ必要があるならConnect、サービス間の通信だけで完結するならgRPCが基本です。gRPCで作ったサービスにブラウザ向けの入口を足す場合も、proto定義はそのまま使えます。ただしgrpc-goなど素のgRPCサーバーはConnectプロトコルを話さないため、サーバーをconnect-goへ移すか、EnvoyのConnect-gRPCブリッジを挟む作業が別に要ります。
PythonでConnectは使えますか?
使えます。公式のconnect-py(PyPIのconnectrpc)が0.12.1のベータで提供されており、WSGI・ASGIのサーバーとクライアント、ストリーミングに対応しています。
connect-goはv1のままでも問題ありませんか?
問題ありません。v1は無期限でバグ修正とセキュリティ修正が続くと公式に告知されており、移行の期限もありません。ただし公式ドキュメントの例はv2に切り替わっているため、新しく書くコードはv2にそろえた方が、資料との食い違いで迷わずに済みます。