【Go】gRPCをJavaScriptからHTTP経由で接続できるようにする


概要

最近gRPCを個人的に触ってみて、Webのページから呼び出しを試してみました。
GolangでgRPCの記事はけっこうあるのですが、Webから使用できるようにするのに少し手間取ったので、今回メモ書きしてみます。

前提など

実装サンプル

<protoの定義>

以下のようなprotoをサンプルとして準備します。
メソッドとして、認証コードをリクエストとして設定しユーザ情報を返すものにしています。

authenticationUser.proto
syntax = "proto3";

option go_package = "pb";
package pb;

service AuthenticationUserService {
  // 認証コードの検証
  rpc VerifyAuthCode (VerifyAuthCodeRequest) returns (UserResponse) {}
}

message VerifyAuthCodeRequest {
  string authCode = 1;
}

message UserResponse {
  string id = 1;
  string name = 2;
}

<サーバサイド>

improbable-eng/grpc-webのライブラリをインストールします。今回使用したバージョンは0.15.0です。
また、CORSの設定も実装する必要があります。詳細はGolangのgrpcwebでCORSエラーが出る時はOriginFuncを確認しようの記事を参照ください。

main.go
package main

import (
	"fmt"
	"log"
	"net/http"
	"os"
	
	"sample-api/src/pb"
	"sample-api/src/service"

	"github.com/improbable-eng/grpc-web/go/grpcweb"
	"google.golang.org/grpc"
)

func main() {
	grpcServer := grpc.NewServer()
	// サービスの設定(サービスの実装は内容割愛)
	pb.RegisterAuthenticationUserServiceServer(grpcServer, service.NewAuthenticationUserService())
	// HTTP経由で接続できるようラップする
	wrappedServer := grpcweb.WrapServer(
		grpcServer,
		// CORSの設定
		grpcweb.WithOriginFunc(func(origin string) bool {
			return origin == "http://localhost:3000"
		}),
	)
	mux := http.NewServeMux()
	mux.Handle("/", http.HandlerFunc(wrappedServer.ServeHTTP))
	// ポート8080で起動
	hs := &http.Server{
		Addr:    ":8080",
		Handler: mux,
	}
	log.Fatal(hs.ListenAndServe())
}

<フロントエンド>

google-protobufgrpc-webのライブラリをインストールします。今回使用したバージョンはgoogle-protobufは3.20.0、grpc-webは1.3.1を使用しました。
また、接続部分の実装にあたりこちらのGitHubのissueの内容を参考にしました。実装してみた内容を以下に記載します。

authenticationUserGrpc.js
import { VerifyAuthCodeRequest } from "../pb/authenticationUser_pb";
import { AuthenticationUserServiceClient } from "../pb/authenticationUser_grpc_web_pb";

// サーバサイドの接続先を設定
const authenticationUserService = new AuthenticationUserServiceClient(
  "http://localhost:8080"
);

// authCodeからgRPC経由でユーザ情報を取得
export async function getUserFromAuthCodeApi(authCode) {
  const request = new VerifyAuthCodeRequest();
  request.setAuthcode(authCode);

  try {
    // 通信部分をPromiseで包む
    const verifyAuthCodePromise = new Promise((resolve, reject) => {
      authenticationUserService.verifyAuthCode(
        request,
        null,
        (err, response) => {
          if (err) {
	    // エラーの場合は成功フラグfalseのオブジェクトを返す
            reject({
              succcess: false,
            });
          } else {
	    // 成功の場合はresponseからユーザ情報を取得
            resolve({
              succcess: true,
              user: {
                id: response.getId(),
                name: response.getName(),
              },
            });
          }
        }
      );
    });
    // Promiseの実行
    return await verifyGoogleAuthCodePromise;
  } catch (_) {
    return { succcess: false };
  }
}

その他参考

grpc-web-clientをGAE/Goで動かしてみた #golang #grpc