GraphQL から gRPC 通信を行う
これは何
今話題の GraphQL から、マイクロサービスでよく使われている gRPC プロトコルでの通信基盤を作ってみた話。
GraphQL とか gRPC については以下のリンクで学べばいいと思う。
いまさらだけど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
があるのではないかと予想できるから、調べてみると案の定発見。
これの通りに実装していくと、 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
配下にあって、サーバー側は ./server
に Golang で gRPC サーバーを実装してある。
それぞれ実行する。クライアントのポートは 3939 でサーバーのポートは 3941 にしてあるから、クライアントの GUI ツールを開いて叩いてみる。
でけた。