🔖

gRPC - connect - Render でwebサービスを作ってみる:local buf build for web service

に公開

背景

gRPCもRemixも触ったことがないので、触ってみたいと思います。
以前、以下の記事でVite+React構成のフロントエンドからgRPCサーバと通信をしてみました。
この時は生成済みのSDKを使っていたのですが、独自のサーバを立てたいので自分で実装したいと思います。

今回使ったコードは以下にあります。

参照情報

ELIZAサービスはプロトコルバッファのスキーマを定義しています。スキーマとは単純なファイルで、サービス・メソッド、引数・戻り値の型、を定義するものです。

syntax = "proto3"

service ElizaService {
    string sentence = 1;
}

message SayRequest {
    string sentence = 1;
}

message SayResponse {
    string sentence = 1;
}

Buf Schema Registry(BSR)でコメントや追加のRPCなども含めたフルバージョンがあります。rpcというのはRemote Procedure Callのことで、遠隔で関数を呼び出すことができます。スキーマは、サーバ・クライアント間の契約で、シリアライズの詳細に至るまでデータ交換の詳細を定義しています。

スキーマはコード生成により使えるようになります。サーバーではインターフェースが生成され、開発者はビジネスロジックの開発に専念できます。クライアント側ではやることはありません。開発者はクライアント関数を呼ぶだけです。コンパイル時に生成される型安全とシリアル化のために作られた型を使ってアプリケーションロジックの開発に集中できます。

生成済みSDK

以前生成済みSDKnpm installでインストールして使用しました。NMPレジストリのBSRでパッケージがリクエストされると、コードジェネレータを通してスキーマが実行され、すべての必要な依存関係と合わせて準備されます。

BSRに公開されたスキーマを使うConnectやgRPCサービスを使いたい場合は、npmでパッケージをインストールし、Connect clientでサービスにアクセスするだけでよいです。生成済みSDKのドキュメントはこちらを見てください。

Localで生成する

Bufを使ってコード生成します。Googleのprotobuf compilerの代替手段であるBufとECMAScriptのコンパイラープラグインでコードを生成します。

  • @bufbuild/buf - Protobufファイルをコンパイルし多くの言語向けのコードを生成します
  • @bufbuild/protoc-gen-es - ProtobufファイルからTypeScriptかJavaScriptのコードを生成します

生成するコードは以下3つに依存します。

  • @connectrpc/connect - クライアント・インターセプタ・エラー等のConnectを使うための基本的な機能を提供します
  • @connectrpc/connect-web - Webブラウザ向けにConnectとgRPC-webプロトコルを提供します
  • @bufbuild/protobuf - Protobufのシリアライズ機能などを提供します

まず以下をインストールします。

npm install --save-dev @bufbuild/buf @bufbuild/protoc-gen-es
npm install @connectrpc/connect @connectrpc/connect-web @bufbuild/protobuf

次にBufに2つのプラグインを使うように設定します。

# buf.gen.yaml defines a local generation template.
# For details, see https://buf.build/docs/configuration/v2/buf-gen-yaml
version: v2
plugins:
  # This will invoke protoc-gen-es and write output to src/gen
  - local: protoc-gen-es
    out: src/gen
    # Also generate any imported dependencies
    include_imports: true
    # Add more plugin options here
    opt: target=ts

localプラグインを使わずにリモートプラグインを使うこともできます。リモートプラグインのbuf.gen.yamlのサンプルはconnect-es exampleを見てください。

最後にBufでELIZAスキーマを作りましょう。

npx buf generate buf.build/connectrpc/eliza

出力

生成されたファイルを見てみましょう。src/gen/connectrpc/eliza/v1/eliza_pb.tsが作られていて、以下のサービスを定義しています。

export const ElizaService: GenService<{
  say: {
    methodKind: "unary";
    input: typeof SayRequestSchema;
    output: typeof SayResponseSchema;
  },
}> = serviceDesc(file_connectrpc_eliza_v1_eliza, 0);

生成物について詳細に知りたい場合はProtobuf-ES projectを参照してください。

localファイルを使う

localで生成したファイルを使うには以下のようにパスを変更します。

- import { ElizaService } from "@buf/connectrpc_eliza.bufbuild_es/connectrpc/eliza/v1/eliza_pb";
+ import { ElizaService } from "./gen/connectrpc/eliza/v1/eliza_pb";

動作確認

前回と同じくプロジェクトでサーバーを起動します。

npm run dev

前回同様実行できました!

今回はVite+Reactの環境におけるconnectのコードジェネレートをlocalで実行できました。これで自分だけのサービスを作る時は自由に定義・ビルド・利用できそうです 👍

Discussion