connect-web の記事が、はてブでトレンドになっていました。気になったので、試してみました。
サンプルコードは、次のリポジトリに置いています。
RPC (Remote Procedure Call) を実現するためのプロトコルとして、gRPC があります。 このプロトコルは、ブラウザ側からは使えない(?)ため、gRPC-Web というブラウザ向けの gRPC というものを使うことになります。 その場合、ブラウザとサーバーとの間に、プロキシを建てる必要があるようです。(たぶん)
そこで、Connect という gRPC 互換の HTTP API を構築するためのライブラリ群が開発されました。 これのおかげで、プロキシを建てる必要がなく、ブラウザ側から gRPC を使うことが可能になります。
上記ページに、バックエンドは connect-go、フロントエンドは connect-web という項目があります。 connect-web は、ブラウザから RPC を動かすための小さなライブラリです。タイプセーフなライブラリなため、 型補完が効きます。 connect-go は、go で Connect のサービスを作ることができます。
そのため、フロントエンドの開発は、connect-web を使うことになります。 以降は、フロントエンドの作業を、紹介します。ちなみに、React を使います。
フロントエンド側は、主に、次の 2 つの作業になります。
gRPC で通信するためのスキーマ、ProtocolBuffer スキーマが必要です。 これは、すでにあるものを使います。
具体的には、次のようなスキーマです。
syntax = "proto3";
service ElizaService {
rpc Say(SayRequest) returns (SayResponse) {}
}
message SayRequest {
string sentence = 1;
}
message SayResponse {
string sentence = 1;
}TypeScript コードを生成するために、buf という CLI を使います。
buf で利用する、次の定義ファイルを書きます。
## buf.gen.yaml
## buf.gen.yaml defines a local generation template.
## For details, see https://docs.buf.build/configuration/v1/buf-gen-yaml
version: v1
plugins:
- name: es
path: node_modules/.bin/protoc-gen-es
out: gen
# With target=ts, we generate TypeScript files.
# Use target=js+dts to generate JavaScript and TypeScript declaration files
# like remote generation does.
opt: target=ts
- name: connect-web
path: node_modules/.bin/protoc-gen-connect-web
out: gen
# With target=ts, we generate TypeScript files.
opt: target=tsこれは、後述する buf generate するときにどういう出力をするかの設定情報です。
codegen の yaml ファイルみたいなものかなと思います。
これを動かすために、次の module をインストールしましょう。
## plugin
yarn add --dev @bufbuild/protoc-gen-connect-web @bufbuild/protoc-gen-es
## runtime
yarn add @bufbuild/connect-web @bufbuild/protobuf次に、bufをインストールしましょう。
私は、brew でインストールしました。
brew install bufbuild/buf/buf
## ref: https://github.com/bufbuild/buf#installationでは、ProtocolBuffer スキーマから TypeScript ファイルを生成しましょう。
buf generate --template buf.gen.yaml buf.build/bufbuild/eliza成功すると、次の 2 つの TypeScript ファイルが生成されます。
eliza_connectweb.tsは、次のコードが含まれています。
// eliza_connectweb.ts
import { SayRequest, SayResponse } from "./eliza_pb.js";
import { MethodKind } from "@bufbuild/protobuf";
export const ElizaService = {
typeName: "ElizaService",
methods: {
say: {
name: "Say",
I: SayRequest,
O: SayResponse,
kind: MethodKind.Unary,
},
},
} as const;eliza_pb.tsは、次のコードが含まれています。
export class SayRequest extends Message<SayRequest> {
/**
* @generated from field: string sentence = 1;
*/
sentence = "";
constructor(data?: PartialMessage<SayRequest>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime = proto3;
static readonly typeName = "buf.connect.demo.eliza.v1.SayRequest";
# ... 省略 ...
}
/**
* SayResponse describes the sentence responded by the ELIZA program.
*
* @generated from message buf.connect.demo.eliza.v1.SayResponse
*/
export class SayResponse extends Message<SayResponse> {
/**
* @generated from field: string sentence = 1;
*/
sentence = "";
constructor(data?: PartialMessage<SayResponse>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime = proto3;
static readonly typeName = "buf.connect.demo.eliza.v1.SayResponse";
# ... 省略 ...
}これで、準備はできました。
では、gRPC のクライアントを実装しましょう。
gRPC のクライント生成は、createPromiseClient でできます。
生成時の引数に、サービスとトランスポート(?)というものを渡す必要があります。
コードを見たほうがわかりやすいと思うので、次のコードを見てください。
// client.ts
import { useMemo } from "react";
import { ServiceType } from "@bufbuild/protobuf";
import {
createConnectTransport,
createPromiseClient,
PromiseClient,
Transport,
} from "@bufbuild/connect-web";
const transport = createConnectTransport({
baseUrl: "https://demo.connect.build", # バックエンド側のURL
});
export function useClient<T extends ServiceType>(service: T): PromiseClient<T> {
return useMemo(() => createPromiseClient(service, transport), [service]);
}このクライアントを、使ってみましょう。
// App.tsx
import { createConnectTransport, Interceptor } from "@bufbuild/connect-web";
import { ElizaService } from "../gen/buf/connect/demo/eliza/v1/eliza_connectweb";
import { useClient } from "./client";
function App() {
const client = useClient(ElizaService);
client
.say({
sentence: "hello",
})
.then(({ sentence }) => {
console.log(sentence);
});
// ...
}このように、ProtocolBuffers の ElizaService が、型補完として使えるようになります。 良い感じです!
意外とあっさり動いて、びっくりしました。
-
タグ「バックエンド」の記事
はじめに tRPCは、型安全なAPIを簡単に構築できるフレームワークです。開発中、バックエンドの実装を待たずに、Storybook上でフロントエンドの開発を進めたい場合、Mock Service Worker (MSW) を使用してAPIのモックを行うことができます。この記事では、maloguertin/msw-trpc を用いて、tRPC通信をMSWでモックする方法について解説します。実用例として、サンプルコードをGitHubリポジトリ silverbirder/trpc-msw-storybook-nextjs で共有しています。
Stable Diffusion は、文章を渡すと画像を生成してくれる AI で OSS です。これを自前で動かそうとすると、GPU が必要になります。
タグ「フロントエンド」の記事
最近、ヒューマンインターフェース ガイドライン(HIG)という言葉を知りました。 「ヒューマンインターフェイスガイドライン」には、どのAppleプラットフォームでも優れた体験を設計できるようにするためのガイドとベストプラクティスが含まれてい
2026-01-24
主にWeb関連の個人開発をしている際に心がけていることを書きます。 月末に近づくにつれ、AIの利用上限に達してしまうことがあります。 その状況になった時、以下のいずれかの選択肢が私の中では残っています。 課金して利用上限を増やす 無料モデル
個人サイトをリニューアルをしています。 ノート風のデザインを目指して、スタイルを調整していました。 ノートの見た目は、現実にあるノートを再現しようとCSSを書いていました。 現在、以下の画像のようなノートになっています。 ノート風デザインの
2026-01-20
タグ「クローリング」の記事
zodのrefineを使っていたのですが、pathの使い方を全く理解できておらず、小一時間ほどハマってしまったことがあったので、備忘録として残しておきます。
WikiWikiWeb というコンセプトが好きで、そのコンセプトが含まれている Obsidian や Scrapbox が好きです。Obsidian には、obsidian-gitという Git 連携のプラグインがあります。こちらには、デスクトップだけでなく、モバイルからでも Git Commit できるようになりました。