TypeScript

gRPC×Connect-ESをRemixで使う実装|React Router v8のloaderとRemix 3から呼ぶ【2.3.0実測】

gRPC×Connect-ESをRemixで使う実装|React Router v8のloaderとRemix 3から呼ぶ【2.3.0実測】

RemixからgRPCのサービスを呼ぶなら、組みやすいのはConnect-ES(TypeScript版のConnect)です。Connectのサーバーは1つの実装でConnect・gRPC・gRPC-Webの3プロトコルを受け付けるので、サーバー側からもブラウザからも同じprotoの型で呼べます。

2026年11月時点で「Remix」は2系統あります。Remix v2の後継であるReact Router v8と、2026年10月に正式版が出たRemix 3です。本記事では、Connect-ES 2.3.0でのコード生成・サーバー・認証と、両方のRemixからの呼び出しを、実際に動かしたコードと出力で示します。

まとめ:Connect-ESでRemixからgRPC互換APIを呼ぶ要点

  • コード生成は@bufbuild/protoc-gen-esの1本で足ります。v2以降はサービス定義も*_pb.tsに出力され、protoc-gen-connect-esは使いません。
  • サーバーはconnectNodeAdapterで立てます。HTTP/1.1のnode:httpではgRPCクライアントだけがつながりません(実測でProtocol error)。gRPCの呼び出し元があるならHTTP/2で待ち受けます。
  • 認証はinterceptorでなくrequestGateで行い、readMaxBytesで受け付けるメッセージの大きさを制限します。どちらも指定しないと、未認証のリクエストも任意の大きさの本文も読み込まれます。
  • React Router v8ではloaderから、Remix 3ではルートハンドラーから、サーバー側のConnectクライアントを呼ぶ構成を基本にします。CORSが不要で、APIの接続先をブラウザに出しません。
  • 入力に合わせた再取得やキャッシュが要る画面だけ、ブラウザから@connectrpc/connect-queryで直接呼びます。

Remix×Connectの構成と2026年11月時点の前提

gRPCはHTTP/2のトレーラーに依存するため、ブラウザのfetchからは素のgRPCを呼べません。Connectは、同じサーバーがConnect・gRPC・gRPC-Webの3種類を話すことでこの制約を避けます。gRPCそのものの仕組みはgRPCの仕組みとTCP・HTTP/3での実測、Connectの位置づけとGo実装はConnect(connectrpc)とconnect-go v2の解説にまとめています。

サーバーの待ち受け方式別のプロトコル到達性

Connect-Nodeのサーバーを3つの方式で立て、Node.jsのクライアントから各Transportで接続した結果です。ブラウザの列だけはConnect-Nodeの公式ドキュメントの記述によります。

待ち受け方式 Connect gRPC-Web gRPC ブラウザ
node:http(HTTP/1.1) 成功 成功 Protocol error 接続可
node:http2 平文(h2c) 成功 成功 成功 接続不可
node:http2 TLS+allowHTTP1 成功 成功 成功 接続可

公式ドキュメントは、HTTP/1.1ではgRPCと双方向ストリーミングを提供できず、ブラウザは平文のHTTP/2を拒否すると明記しています。本記事のunary RPCをConnectまたはgRPC-Webで呼ぶ構成なら、HTTP/1.1で接続できます。loaderからでも、gRPCや双方向ストリーミングを使う場合はHTTP/2が必要です。

「Remix」が指すReact Router v8とRemix 3

Remixチームは2024年5月15日、Remix v3として予定していた機能をReact Router v7として出すと発表し、Remix v2のアプリはReact Routerへ移りました。現行はReact Router v8で、npmの公開日時は8.0.0が2026年6月17日(UTC)です。検証に使った8.5.1は、package.jsonでNode.js 22.22.0以上を要求します。loaderとRoute型を使う書き方はこちらの系統です。

Remix 3はnpmのremixパッケージとして配布され、公開日時は3.0.0が2026年10月1日、3.2.0が11月4日です(いずれもUTC)。Reactに依存しない独自のコンポーネントと、fetch APIのRequestを受けてResponseを返すルーターで組む別系統で、Node.js 24.3.0以上を要求します。Remix 3のコンポーネントの書き方はRemix 3のコンポーネントの解説、Remix v2からReact Routerへの移行はRemix SPAモードとReact Router v8への移行の解説で扱っています。

検証に使ったバージョン

本記事のコードと出力は、2026年11月5日にNode.js 26.5.0で実行したものです。

パッケージ バージョン
@connectrpc/connect/connect-node/connect-web 2.3.0
@bufbuild/protobuf/protoc-gen-es 2.16.0
@bufbuild/buf 1.75.0
react-router/@react-router/dev 8.5.1
remix 3.2.0
@connectrpc/connect-query 2.3.1
@tanstack/react-query 5.106.0

@connectrpc/connect-nodeはpackage.jsonでNode.js 22以上を要求します。Node.js 20のサポートは2.2.0で打ち切られました。

Connect-ES v2系のコード生成手順(protoc-gen-connect-es不要)

Connect-ESの実行依存とコード生成用パッケージ

実行時のライブラリと、コード生成用の開発依存を分けて入れます。

npm install @connectrpc/[email protected] @connectrpc/[email protected] @connectrpc/[email protected] @bufbuild/[email protected]
npm install -D @bufbuild/[email protected] @bufbuild/[email protected] [email protected]

protoとbuf.gen.yamlの書き方

名前を受け取って挨拶を返す1メソッドのサービスを定義します。idempotency_levelは後述するGETリクエストのための指定です。

// proto/greet/v1/greet.proto
syntax = "proto3";

package greet.v1;

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でモジュールとLintの規則を、buf.gen.yamlで生成プラグインを指定します。プラグインはprotoc-gen-esの1つだけです。

# buf.yaml
version: v2
modules:
  - path: proto
lint:
  use:
    - STANDARD
breaking:
  use:
    - FILE
# buf.gen.yaml
version: v2
inputs:
  - directory: proto
plugins:
  - local: protoc-gen-es
    out: src/gen
    opt: target=ts
npx buf lint
npx buf generate

生成されるのはsrc/gen/greet/v1/greet_pb.tsの1ファイルで、メッセージのスキーマとGreetServiceのサービス定義が同じファイルに入ります。v1時代の*_connect.tsは出力されません。

v1からの移行で変わる点

Connect-ES 2.0.0は2024年11月19日(UTC)にGitHubで公開されました。公式の移行ガイドは、protoc-gen-connect-esをpackage.jsonとbuf.gen.yamlの両方から削除するよう求めています。npmには@connectrpc/protoc-gen-connect-es 1.7.0が残っており、npmのページだけを見ると現役に見えますが、v2のランタイムとは組み合わせません。

  • 依存とbuf.gen.yamlの書き換えはnpx @connectrpc/connect-migrate@latestが自動で行います。
  • import先は*_connectから*_pbに変わります。
  • メッセージはES6クラスでなくプレーンなオブジェクトになり、loaderの戻り値としてそのままシリアライズできます。

Connect-Nodeサーバーの実装とcurlでの動作確認

サービス実装とconnectNodeAdapterの起動

サービスの実装はconnect.tsに分けておくと、後述のHTTP/2版やRemix 3との同居でも同じものを使い回せます。入力が不正なときはConnectErrorにコードを付けて投げます。

// src/connect.ts
import type { ConnectRouter } from "@connectrpc/connect";
import { ConnectError, Code } from "@connectrpc/connect";
import { GreetService } from "./gen/greet/v1/greet_pb";

export default (router: ConnectRouter) =>
  router.service(GreetService, {
    async greet(req) {
      if (req.name === "") {
        throw new ConnectError("name is required", Code.InvalidArgument);
      }
      return { greeting: `Hello, ${req.name}` };
    },
  });

Node.js標準のnode:httpにconnectNodeAdapterを渡すと、HTTP/1.1でConnectとgRPC-Webを受け付けるサーバーになります。

// src/server.ts
import { createServer } from "node:http";
import { connectNodeAdapter } from "@connectrpc/connect-node";
import { ConnectError, Code, type HandlerContext } from "@connectrpc/connect";
import routes from "./connect";

const apiToken = process.env.API_TOKEN;
if (!apiToken) {
  throw new Error("API_TOKEN must be set");
}

function authGate(context: HandlerContext) {
  if (context.requestHeader.get("Authorization") !== `Bearer ${apiToken}`) {
    throw new ConnectError("unauthenticated", Code.Unauthenticated);
  }
}

createServer(
  connectNodeAdapter({
    routes,
    requestGate: authGate,
    readMaxBytes: 1024 * 1024, // 1メッセージ1MiBまで
  }),
).listen(8080);

requestGateによる認証とreadMaxBytesの上限

上のserver.tsは、Authorizationヘッダーのトークンが一致しないリクエストをrequestGateで断ります。API_TOKENが未設定のまま起動すると比較相手がBearer undefinedになり、その文字列を送れば通ってしまうため、起動時に止めています。Connect-ESのinterceptorのドキュメントは、サーバーの認証にinterceptorを使わないよう求めています。unaryのRPCでは、interceptorはリクエスト本文を受信・展開・パースした後に動くためです。requestGateはヘッダーが届いた時点で動き、ConnectErrorを投げると本文を読まずにRPCを終えます。

リリースノートによると、requestGateは2026年9月7日の2.2.0で追加されました。同じリリースでメッセージサイズの上限設定も推奨されています。サーバーは既定で任意の大きさのメッセージを受け付けるため、外部に公開するならreadMaxBytesを指定します。

リクエスト 結果のコード
トークンなし unauthenticated(HTTP 401)
2MiBのname(上限1MiB) resource_exhausted
正しいトークン・通常のname 成功(HTTP 200)

2MiBの例では、エラーメッセージにmessage size is larger than configured readMaxBytes 1048576が返りました。ゲートで決めたユーザー情報をサービス実装へ渡すには、公式ドキュメントにあるcontext valuesを使います。

curlによるPOST・GET・エラーレスポンスの確認

APIプロジェクトのルートでAPI_TOKEN=secret-token npx tsx src/server.tsを実行してサーバーを起動し、別のターミナルで同じAPI_TOKENを設定して以下のcurlを実行します。コメントはレスポンス例で、ステータス行も見るなら-iを付けます。パスは/パッケージ名.サービス名/メソッド名です。

curl -X POST http://localhost:8080/greet.v1.GreetService/Greet \
  -H "Content-Type: application/json" -d '{"name":"Remix"}'
# HTTP/1.1 401 Unauthorized
# {"code":"unauthenticated","message":"unauthenticated"}

curl -X POST http://localhost:8080/greet.v1.GreetService/Greet \
  -H "Content-Type: application/json" -H "Authorization: Bearer $API_TOKEN" \
  -d '{"name":"Remix"}'
# HTTP/1.1 200 OK
# {"greeting":"Hello, Remix"}

curl -X POST http://localhost:8080/greet.v1.GreetService/Greet \
  -H "Content-Type: application/json" -H "Authorization: Bearer $API_TOKEN" -d '{}'
# HTTP/1.1 400 Bad Request
# {"code":"invalid_argument","message":"name is required"}

NO_SIDE_EFFECTSを付けたメソッドはGETでも呼べます。リクエストはクエリ文字列に入るため、CDNやブラウザのHTTPキャッシュに載せられます。ただしGETにするだけでは保存されず、サーバーでCache-Controlなどの応答ヘッダーを設定する必要があります。ConnectクライアントからGETを使う場合は、TransportのuseHttpGetも有効にします。

curl -H "Authorization: Bearer $API_TOKEN" \
  'http://localhost:8080/greet.v1.GreetService/Greet?connect=v1&encoding=json&message=%7B%22name%22%3A%22Remix%22%7D'
# HTTP/1.1 200 OK
# {"greeting":"Hello, Remix"}

同じprotoからidempotency_levelだけを外して生成し直すと、同じGETは405 Method Not Allowedになり、POSTは200のままでした。

gRPCクライアントも受けるHTTP/2の待ち受け構成

Goのサービスなど、素のgRPCで呼んでくる相手がいる場合はnode:http2で立てます。HTTP/1.1のサーバーにcreateGrpcTransportで接続すると[internal] Protocol errorで失敗し、次のHTTP/2サーバーでは同じ呼び出しが成功しました。

// src/server_h2.ts
import { createServer } from "node:http2";
import { connectNodeAdapter } from "@connectrpc/connect-node";
import routes from "./connect";

createServer(connectNodeAdapter({ routes })).listen(8081);

これは平文のHTTP/2(h2c)で、ブラウザはつながりません。ブラウザとgRPCクライアントを同じポートで受けるなら、TLSを付けてallowHTTP1を有効にします。検証ではこの1ポートで、gRPCとHTTP/1.1のConnect・gRPC-Webがすべて成功しました。例ではlocalhost用の自己署名証明書と鍵をopensslで作ってcerts/に置き、Node.jsのクライアントにはTransportのnodeOptionsのcaでその証明書を渡しています。ブラウザからも使うなら、公式のGetting startedが案内するmkcertで、ローカルで信頼される証明書を作ります。

// src/server_tls.ts
import { readFileSync } from "node:fs";
import { createSecureServer } from "node:http2";
import { connectNodeAdapter } from "@connectrpc/connect-node";
import routes from "./connect";

createSecureServer(
  {
    key: readFileSync("certs/key.pem"),
    cert: readFileSync("certs/cert.pem"),
    allowHTTP1: true, // HTTP/1.1しか話さないクライアントも同じポートで受ける
  },
  connectNodeAdapter({ routes }),
).listen(8443);

Node.js側のgRPCクライアントには、2.3.0(2026年10月27日)で接続確立のタイムアウトconnectTimeoutMsが加わりました。応答しないアドレスに向けると、指定した時間でunavailableのエラーになります。

const transport = createGrpcTransport({
  baseUrl: "http://10.255.255.1:8081",
  connectTimeoutMs: 1000,
});
// 1秒でConnectError [unavailable] connection establishment timed out

React Router v8のloaderからConnectクライアントを呼ぶ実装

SSRを有効にしたReact Router v8のFramework Modeプロジェクトを前提にします。先ほど生成したsrc/genを、このプロジェクトのapp/genへコピーしてください。ルートはapp/routes.tsに登録します。

// app/routes.ts
import { type RouteConfig, index, route } from "@react-router/dev/routes";

export default [
  index("routes/home.tsx"),
  route("client-greet", "routes/client-greet.tsx"),
] satisfies RouteConfig;

サーバー専用のクライアントモジュール

Transportはリクエストごとに作らず、モジュールで1つ作って使い回します。サーバー側では@connectrpc/connect-nodeのTransportを使い、httpVersionを明示します。

// app/lib/greet.server.ts
import { createClient } from "@connectrpc/connect";
import { createConnectTransport } from "@connectrpc/connect-node";
import { GreetService } from "../gen/greet/v1/greet_pb";

const transport = createConnectTransport({
  baseUrl: process.env.GREET_API_URL ?? "http://localhost:8080",
  httpVersion: "1.1",
});

export const greetClient = createClient(GreetService, transport);

ファイル名を.server.tsにしておくと、誤ってブラウザ側から使ったときにビルドが止まります。ボタンのクリック処理からgreetClientを呼ぶルートを足すと、react-router buildはServer-only module referenced by clientで失敗しました。loaderからだけ使う構成では、クライアント向けの成果物に接続先の文字列は残りませんでした。

loaderでの呼び出しとエラーのHTTPステータス変換

loaderは受け取ったrequest.signalをRPCに渡します。ユーザーが画面遷移で読み込みを中断すると、バックエンドへの呼び出しも止まります。トークンはサーバーの環境変数から付けるので、ブラウザには出ません。

// app/routes/home.tsx
import { ConnectError, Code } from "@connectrpc/connect";
import { data } from "react-router";
import type { Route } from "./+types/home";
import { greetClient } from "../lib/greet.server";

export async function loader({ request }: Route.LoaderArgs) {
  const name = new URL(request.url).searchParams.get("name") ?? "";
  try {
    const res = await greetClient.greet(
      { name },
      {
        signal: request.signal,
        headers: { Authorization: `Bearer ${process.env.API_TOKEN}` },
      },
    );
    return { greeting: res.greeting };
  } catch (err) {
    const e = ConnectError.from(err);
    if (e.code === Code.InvalidArgument) {
      throw data(e.rawMessage, { status: 400 });
    }
    throw e;
  }
}

export default function Home({ loaderData }: Route.ComponentProps) {
  return <p>{loaderData.greeting}</p>;
}

react-router typegenで型を生成し、ビルドしてreact-router-serveで起動した結果、/?name=Remixは200でHello, Remixを表示し、nameなしは400を返しました。

変換先のHTTPステータスは、Connectプロトコルの対応表に合わせると画面側の分岐が揃います。loaderで扱うことの多いコードは次のとおりです。全16種はgRPCステータスコードの対応表を参照してください。

Connectのコード HTTPステータス
invalid_argument 400
unauthenticated 401
permission_denied 403
not_found 404
resource_exhausted 429
unavailable 503
deadline_exceeded 504

Remix 3のルートハンドラーからConnectを呼ぶ実装

Remix 3にはloaderがありません。ルーターに登録したハンドラーがRequestを受け取り、Responseを返します。Connectのクライアントはハンドラーの中で呼べばよく、React Router v8と同じconnect-nodeのTransportがそのまま使えます。

remix/routerのハンドラーからのConnect呼び出し

次のファイルは、ハンドラーからの呼び出しと、後述する同一ポートでの配信を1つにまとめたものです。npm install [email protected]を追加し、API_TOKEN=secret-token npx tsx src/remix3.tsで起動します。

// src/remix3.ts
import { createServer, type IncomingMessage, type ServerResponse } from "node:http";
import { createClient, ConnectError, Code } from "@connectrpc/connect";
import { createConnectTransport, connectNodeAdapter } from "@connectrpc/connect-node";
import { createRequestListener } from "remix/node-fetch-server";
import { createRouter } from "remix/router";
import { route } from "remix/routes";
import { html } from "remix/html-template";
import { createHtmlResponse } from "remix/response/html";
import { GreetService } from "./gen/greet/v1/greet_pb";
import connectRoutes from "./connect";

const greetClient = createClient(
  GreetService,
  createConnectTransport({
    baseUrl: process.env.GREET_API_URL ?? "http://localhost:8080",
    httpVersion: "1.1",
  }),
);

const routes = route({ home: "/" });
const router = createRouter();

router.get(routes.home, async ({ request, url }) => {
  const name = url.searchParams.get("name") ?? "";
  try {
    const res = await greetClient.greet(
      { name },
      {
        signal: request.signal,
        headers: { Authorization: `Bearer ${process.env.API_TOKEN}` },
      },
    );
    return createHtmlResponse(html`<p>${res.greeting}</p>`);
  } catch (err) {
    const e = ConnectError.from(err);
    const status = e.code === Code.InvalidArgument ? 400 : 502;
    return createHtmlResponse(html`<p>${e.rawMessage}</p>`, { status });
  }
});

const remixListener = createRequestListener((request) => router.fetch(request));

// RPCのパスはConnectが処理し、それ以外はRemix 3のルーターへ渡す
// node:httpで立てるので、fallbackに来るのはHTTP/1.1のリクエストだけ
createServer(
  connectNodeAdapter({
    routes: connectRoutes,
    fallback: (req, res) =>
      remixListener(req as IncomingMessage, res as ServerResponse),
  }),
).listen(3000);

ハンドラーはrequestとパース済みのurlを受け取り、request.signalを渡す点はloaderと同じです。HTMLはremix/html-templateのhtmlタグで組み立てます。埋め込んだ値は自動でエスケープされ、nameに<b>x</b>を渡すとHello, &lt;b&gt;x&lt;/b&gt;として返りました。この例で利用者の入力を含むRPCの結果をエスケープせずHTMLへ埋め込むと、反射型XSSを招く可能性があります。

検証では、/?name=Remixが200でHello, Remixを、nameなしが400を返しました。

Connectのサーバーと同じポートで配信する構成

connectNodeAdapterのfallbackオプションは、RPCのパスに一致しなかったリクエストの行き先を決めます。ここにremix/node-fetch-serverのcreateRequestListenerでRemix 3のルーターを渡すと、1つのポートで画面とRPCの両方を配信できます。上の例では、/greet.v1.GreetService/GreetへのPOSTはConnectが200で応答し、/はRemix 3の画面を、登録していないパスは404を返しました。

例のハンドラーは、別サービスとして動くAPIを呼ぶ想定で接続先をGREET_API_URLから読みます。この構成ではブラウザから同一オリジンでRPCを呼べるため、CORSの設定が要りません。ただし公開したRPCはブラウザから直接叩けるので、外部に出すなら前述のrequestGateを同じアダプターに足します。node:httpで立てているため、gRPCクライアントの受け口にはなりません。

ブラウザからconnect-queryで直接呼ぶ設定

TransportProviderとuseQueryの組み込み

connect-query v2は、TanStack QueryのuseQueryに、クエリキーと取得関数を自動で割り当てるラッパーです。ブラウザでは@connectrpc/connect-webのTransportを渡します。追加の生成プラグインは要らず、GreetService.method.greetをそのまま渡せます。React Router v8のルートとして書いた例です。

npm install @connectrpc/[email protected] @tanstack/[email protected]
// app/routes/client-greet.tsx
import { createConnectTransport } from "@connectrpc/connect-web";
import { TransportProvider, useQuery } from "@connectrpc/connect-query";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { useState } from "react";
import { GreetService } from "../gen/greet/v1/greet_pb";

const transport = createConnectTransport({ baseUrl: "https://api.example.com" });

function Greeting({ name }: { name: string }) {
  const { data, error, isPending } = useQuery(GreetService.method.greet, { name });
  if (isPending) return <p>loading...</p>;
  if (error) return <p>{error.code}: {error.rawMessage}</p>;
  return <p>{data.greeting}</p>;
}

export default function ClientGreet() {
  const [queryClient] = useState(() => new QueryClient());
  return (
    <TransportProvider transport={transport}>
      <QueryClientProvider client={queryClient}>
        <Greeting name="Remix" />
      </QueryClientProvider>
    </TransportProvider>
  );
}

errorはConnectError型なので、error.codeで分岐できます。

別オリジンのAPIを呼ぶときのCORS

別オリジンへJSONのPOSTや独自ヘッダー付きのリクエストを送ると、ブラウザは事前にプリフライト(OPTIONS)を送ります。@connectrpc/connectが公開するcorsオブジェクトには、Connectの3プロトコルに必要なallowedMethods・allowedHeaders・exposedHeadersが定数で入っています。CORSミドルウェアにこれを渡し、Authorizationなどアプリ固有のヘッダーを足します。

gRPC-Webで既存のgRPCサーバーにつなぐ場合は、Envoyのgrpc_webフィルタなどの変換プロキシが必要です。構成はgRPC-Webの仕組みとプロキシ構成の解説で扱っています。

サーバー経由とブラウザ直呼びの使い分けの判断基準

既定はサーバー経由(React Router v8のloader、Remix 3のルートハンドラー)です。ブラウザからの直呼びは、理由がある画面に限ります。

観点 サーバー経由 ブラウザ直呼び
CORS設定 不要 別オリジンなら必要
APIの接続先・トークン サーバー内に閉じる ブラウザに出る
初回HTMLへのデータ(掲載例) 含まれる 含まれない
使えるプロトコル 3種すべて ConnectとgRPC-Web
再取得の単位 遷移・再検証ごと クエリごと

ブラウザ直呼びを選んでよいのは、検索条件の入力に合わせて頻繁に再取得する画面や、同じデータを複数の部品で共有してキャッシュしたい画面です。社内のgRPCサービスをそのまま外へ出す目的で直呼びを選ぶのは避けます。認証・レート制限・CORSをバックエンドで新たに抱え、サーバー側で1層挟めば済む問題を広げるためです。外部公開のAPI設計から相談したい場合は、API開発・システム連携の支援も行っています。

よくある失敗は3つです。HTTP/1.1で立てたサーバーにgRPCのTransportを向けてProtocol errorになるもの、v1の記事を見てprotoc-gen-connect-esを入れ、v2のランタイムと生成物が噛み合わなくなるもの、そしてinterceptorで認証して、未認証のリクエストの本文まで読み込んでしまうものです。tRPCなど他の型安全なRPCとの比較はtRPCとgRPCの違いの解説にあります。

よくある質問

protoc-gen-connect-esはまだ使えますか?

Connect-ES v2以降では使いません。v2.0.0でリポジトリから削除され、サービス定義は@bufbuild/protoc-gen-esが生成します。npmに残る1.7.0はv1系のランタイム向けです。

connect-esとgRPC-Webは何が違いますか?

connect-esはTypeScript向けのConnect実装で、gRPC-Webはプロトコルの1つです。connect-esのブラウザ用パッケージは、ConnectプロトコルのcreateConnectTransportとgRPC-WebのcreateGrpcWebTransportを両方提供します。

RemixのloaderからgRPCサーバーを直接呼べますか?

呼べます。loaderはサーバーで動くので、@connectrpc/connect-nodeのcreateGrpcTransportで素のgRPCサーバーにも接続できます。相手のサーバーがHTTP/2で待ち受けていることが条件です。

Remix 3でもConnect-ESは使えますか?

使えます。Remix 3.2.0のルートハンドラーからconnect-nodeのクライアントで呼べることと、connectNodeAdapterのfallbackでRemix 3と同じポートに同居させられることを確認しました。Remix 3はNode.js 24.3.0以上が必要です。

ExpressやNext.jsでもConnectサーバーを動かせますか?

動かせます。@connectrpc/connect-express・connect-fastify・connect-nextのアダプターがあり、ルートの定義はconnectNodeAdapterと共通です。ただしExpressとNext.jsではgRPCプロトコルを受けられず、ConnectとgRPC-Webに限られます。

関連記事

お気に入りに入れた記事の一覧

一創の公式 SNS

X では毎朝の新着記事と IT ニュースの速報を、YouTube・TikTok・Instagram・LinkedIn・Facebook では記事の解説動画を配信しています。

この記事は以下の記事からリンクされています

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.11.06 テックブログ ドコモ不正アクセス:最大29.5万件の流出項目と見舞金、My docomo停止の経過と偽連絡の見分け方
  2. 2026.11.03 テックブログ ネットマイル不正アクセスで最大549万人:対象確認と退会会員データを消す実装
  3. 2026.11.06 テックブログ あいおいニッセイ同和の不正アクセス:44.4万件の漏えい項目と窓口、止まった手続きの代わり
  4. 2026.11.07 コラム サイバーセキュリティ緊急対策パッケージとは?4本柱の施策と期限、企業と委託先が今やること
  5. 2026.11.06 テックブログ アイリスオーヤマ情報漏洩:アイリスプラザ770万人分の対象項目と本物の連絡の見分け方

RELATED POSTS 関連記事

目次