ホーム自己紹介ブログ
NO.87
DATE2022. 08. 20

connect-webやってみた

connect-web の記事が、はてブでトレンドになっていました。気になったので、試してみました。

サンプルコードは、次のリポジトリに置いています。

  • https://github.com/silverbirder/playground/tree/main/node/connect-web-example/frontend

前置き: gRPC と connect-web の雑な理解

RPC (Remote Procedure Call) を実現するためのプロトコルとして、gRPC があります。 このプロトコルは、ブラウザ側からは使えない(?)ため、gRPC-Web というブラウザ向けの gRPC というものを使うことになります。 その場合、ブラウザとサーバーとの間に、プロキシを建てる必要があるようです。(たぶん)

そこで、Connect という gRPC 互換の HTTP API を構築するためのライブラリ群が開発されました。 これのおかげで、プロキシを建てる必要がなく、ブラウザ側から gRPC を使うことが可能になります。

  • https://connect.build/docs/introduction

上記ページに、バックエンドは connect-go、フロントエンドは connect-web という項目があります。 connect-web は、ブラウザから RPC を動かすための小さなライブラリです。タイプセーフなライブラリなため、 型補完が効きます。 connect-go は、go で Connect のサービスを作ることができます。

そのため、フロントエンドの開発は、connect-web を使うことになります。 以降は、フロントエンドの作業を、紹介します。ちなみに、React を使います。

やってみた

フロントエンド側は、主に、次の 2 つの作業になります。

  1. Protocol Buffer スキーマから TypeScript ファイルを生成
  2. 生成された TypeScript ファイルから gRPC クライアントを実装

1. Protocol Buffer スキーマから TypeScript ファイルを生成

gRPC で通信するためのスキーマ、ProtocolBuffer スキーマが必要です。 これは、すでにあるものを使います。

  • https://buf.build/bufbuild/eliza

具体的には、次のようなスキーマです。

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
  • plugin
    • protoc-gen-es
      • リクエストやレスポンスメッセージのような基本型を生成
    • protoc-gen-connect-web
      • Protocol Buffer スキーマからサービスを生成
  • runtime
    • bufbuild/connect-web
      • Connect および gRPC-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 ファイルが生成されます。

  • gen/buf/connect/demo/eliza/v1/eliza_connectweb.ts
  • gen/buf/connect/demo/eliza/v1/eliza_pb.ts

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";
  # ... 省略 ...
}

これで、準備はできました。

2. 生成された TypeScript ファイルから gRPC クライアントを実装

では、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 が、型補完として使えるようになります。 良い感じです!

終わりに

意外とあっさり動いて、びっくりしました。

バックエンド
フロントエンド
クローリング

-

コメント

0

読み込み中...

シェアする

フォローする

購読する

次のページ

CI/CDのDaggerで、GithubActionsとCircleCIにシュッと連携してみた

前のページ

urqlでデータ変換(transform)してみた

関連する記事

タグ「バックエンド」の記事

Storybook上で tRPC通信をMSWでモックする方法

はじめに tRPCは、型安全なAPIを簡単に構築できるフレームワークです。開発中、バックエンドの実装を待たずに、Storybook上でフロントエンドの開発を進めたい場合、Mock Service Worker (MSW) を使用してAPIのモックを行うことができます。この記事では、maloguertin/msw-trpc を用いて、tRPC通信をMSWでモックする方法について解説します。実用例として、サンプルコードをGitHubリポジトリ silverbirder/trpc-msw-storybook-nextjs で共有しています。

2024年03月07日

テスト
バックエンド
GraphQL Guildのエコシステムって便利だね

GraphQL Guild ってご存知ですか?GraphQL 界隈だと、Code Generator が有名と思いますが

2022年10月15日

バックエンド
Stable Diffusion API 開発

Stable Diffusion は、文章を渡すと画像を生成してくれる AI で OSS です。これを自前で動かそうとすると、GPU が必要になります。

2022年09月03日

AI
バックエンド
成果物

タグ「フロントエンド」の記事

AIの書いたコードの手直しを減らすお作法

AI にコードを書かせた後、余計なコードを見つけて消す作業があります。 不毛なことなので、それらの作業を減らすためのお作法を紹介します。 未使用コードを消す 以下でも書きましたが、未使用コードの検査に knip を使うことが多いです。 ht

2026年02月25日

AI
フロントエンド
iframeの難しさ

最近、iframeを使っています。 クライアントサイドで埋め込む想定で、iframeを使おうとしています。 色々と苦労したことがあったので、書いて残しておこうと思います。 レスポンスヘッダー 前提として、ウェブアプリケーションをプロダクショ

2026年02月18日

フロントエンド
ブラウザ
SVGを書くと数学の知識が必要だった

紙を積んだイラストをSVGで書こうとしていました。 (当たり前ですが)図形を表現するためには数学の知識が必要で、学生の頃の記憶を思い出したので疲れました。 所感について、諸々書こうと思います。 成果物 実際に完成したのは、以下の画像ができま

2026年02月17日

フロントエンド

タグ「クローリング」の記事

zodのrefineにあるpathにハマった

zodのrefineを使っていたのですが、pathの使い方を全く理解できておらず、小一時間ほどハマってしまったことがあったので、備忘録として残しておきます。

2023年01月07日

フロントエンド
クローリング
JavaScript
ObsidianでiPhoneからGit Commitする

WikiWikiWeb というコンセプトが好きで、そのコンセプトが含まれている Obsidian や Scrapbox が好きです。Obsidian には、obsidian-gitという Git 連携のプラグインがあります。こちらには、デスクトップだけでなく、モバイルからでも Git Commit できるようになりました。

2022年10月18日

開発ツール
クローリング
クローリングをシュッとやるのに、Crawleeが便利だった

スクレイピングしたいときって、あると思います。Crawlee という OSS が便利だったので、共有します。

2022年09月14日

クローリング
← ブログ一覧へ