BambTech

コンピュータサイエンスの学習記録等です。

GraphQL から gRPC 通信を行う

これは何

今話題の GraphQL から、マイクロサービスでよく使われている gRPC プロトコルでの通信基盤を作ってみた話。
GraphQL とか gRPC については以下のリンクで学べばいいと思う。

Web API初心者と学ぶGraphQL - Qiita

いまさらだけどgRPCに入門したので分かりやすくまとめてみた - Qiita

GQL サーバー

今回は Apollo GraphQL で実装した。
Apollo については以下のリンクがいいかな。

世のフロントエンドエンジニアにApollo Clientを布教したい - Qiita

GraphQL サーバーの実装は主に3つの要素が必要になってくる。
1. スキーマ: 型定義のようなイメージ
2. データソース: GraphQL を用いてリクエストを送る先をまとめるもの
3. リゾルバー: レスポンスをスキーママッピングするようなもの
gRPC へリクエストを送る GraphQL では、データソースの書き方でちょっと困ったので書く。

データソース

よくあるデータソースでは(REST API を想定) apollo-datasource-rest にある RESTDataSource を継承しているクラス内に API コールをするメソッドを定義していく。

import { RESTDataSource } from 'apollo-datasource-rest');

class LaunchAPI extends RESTDataSource {
  constructor() {
    super();
    this.baseURL = 'https://api.spacexdata.com/v2/';
  }

  async getAllLaunches() {
    const response = await this.get('launches');
    return Array.isArray(response)
      ? response.map(launch => this.launchReducer(launch))
      : [];
  }
}

こんな感じ(公式チュートリアルから)。このメソッドでは、 GET: https://api.spacexdata.com/v2/launches がコールされることとなる。
ここから apollo-datasource-grpc があるのではないかと予想できるから、調べてみると案の定発見。

github.com

これの通りに実装していくと、 return this.callRPC(0, { args: { id }, meta, rpcName: 'GetMovie' }); のところで、rpcName には関数が来るっぽくて通信できなかった。
今回は、データソースを別の形で実装した。

import { meta } from '../config';
import * as protoLoader from '@grpc/proto-loader';
import * as grpc from 'grpc';

const packageDefinition: any = protoLoader.loadSync('../../proto/hello.proto');
const proto: any = grpc.loadPackageDefinition(packageDefinition).hello_test;

const client = new proto.Hello('localhost:3941', grpc.credentials.createInsecure());

export default (root: any, params: any) => {
    return new Promise((resolve: any, reject: any) => {
        client.GetHello(params, meta, function(err: any, response: any) {
            if (err) {
                return reject(err.details);
            }
            resolve({ message: response.msg });
        });
    });
}

gRPC の proto ファイルを loadPackageDefinition(path).package_name で読み取ると、クライアントを作ってくれて、クライアント起点で定義した rpc を読んでくれるようになるみたい。明記はしないけど、整理するならこれがデータソースになるのかなと思う。今回はリゾルバーに無理やり入れたけど。

実際に動かしてみる

proto ファイルは ./proto 配下にあって、サーバー側は ./serverGolang で gRPC サーバーを実装してある。
それぞれ実行する。クライアントのポートは 3939 でサーバーのポートは 3941 にしてあるから、クライアントの GUI ツールを開いて叩いてみる。 f:id:wamiota:20200704182638p:plain でけた。