GoのGraphQLクライアントライブラリ生成ツール「gqlgenc」を作成

山本祥太氏:『gqlgencを作成した』というタイトルで発表していきたいと思います。よろしくお願いします。僕の名前は山本祥太と言います。ふだんは株式会社Appify TechnologiesでGAE(Google App Engine) Goを使ったサーバーサイドの開発や、React、TypeScript、ApolloでWebフロントの開発を行っています。Twitter、GitHubのIDはこちらなので、もしよかったらフォローをよろしくお願いします。

今回の目次はこんな感じになっています。gqlgencというGoのGraphQLクライアントライブラリ生成ツールを作ったという話をベースに、GraphQLとGoの組み合わせについて興味をもってもらおうという構成になっています。

はじめにGraphQLの説明をざっくりして、gqlgencがなぜ必要でどう良いのかを説明したあとに、gqlgencそのものを使ってみるということで今回の発表を締めたいと思います。

GraphQLのメリットと世界におけるトレンド

まず、GraphQLの説明から入っていきたいと思います。GraphQLはFacebookが開発元のAPIクエリ言語であり、ランタイムです。表現力が非常に高いのが特徴で、複雑なデータ構造をスキーマ上で表現することが可能です。

その型をそのままクライアント側も取得でき、それによってクライアント側もサーバー側も多くの恩恵を受けられるのですが、よく言われる恩恵の1つが「オーバーフェッチングを避けられる」です。

従来のものだと、クライアントがAPIを叩く時に不要なデータが入って来ても、リクエストとレスポンスが1対1の対応をしているので、そのまま受け取らなければいけなかったのですが、GraphQLはクエリで定義するので、不要なデータをクエリで定義しなければオーバーフェッチングを避けられます。

次にGraphQLはどのくらい流行っているのかを見たかったので、今回は世界の検索トレンド5年分のグラフを用意しました。gRPCが赤色で、GraphQLが青色で表現されています。これを見るとGraphQLのほうが若干人気があるのかなと思います。

日本だとそこまで差はなくて、ほとんど同じくらいのトレンドに見えます。これは日本の5年間のグラフです。ただ、記事やコミュニティのサイズなどもいろいろと見てみると、gRPCのコミュニティは日本だと特に大きく、GraphQLはあまりないので、まだまだGraphQLはこれからの技術なのかなと感じます。

ただ、さまざまな企業がAPIとして採用していて、GitHubだったりshopifyだったりがGraphQLのAPIを公開しています。それ以外にも多くの事例があるのですが、ここでは紹介しきれないので、見たい方は採用事例がわかるgraphql.org/usersをぜひ見に行ってください。

「gqlgenc」作成のきっかけは「Appify」開発時に生まれた要望

次に、なぜ必要になったのかをお話しします。弊社では「Appify」というサービスを開発しています。過去にもほかにいろいろなサービスを開発をしていたのですが、その頃はprotobuf(Protocol Buffers)をベースに開発していました。protobufからサーバーのコードとクライアントのコードを生成して、クライアントのコードはe2eのクライアントとして使用していました。

Appifyを作るとなった時に、GraphQLにしようとなりましたが、protobufで開発していた頃と同じ開発フローを踏みたいという要望があり、サーバーとクライアントのコードをGraphQLから生成できれば同じようなことができるんじゃないかということで、それを目指していました。

GraphQLの場合はスキーマとクエリが分かれているので、それぞれ役割が違います。スキーマからはサーバーのコードを作る必要があって、クライアントはスキーマを元にクエリが書かれるので、スキーマとクエリからクライアントのコードを生成する必要があります。

サーバーのコードを生成するツールとしてgqlgenはすでに存在していて、スキーマを書いてgqlgenコマンドを叩くとサーバーのコードが生成されます。ただ、クライアントのコードを生成するものは存在しなくて、gqlgenはあくまでもサーバーのコードのみを生成してくれるものなので、クライアントコードには対応していませんでした。

なので、僕はここの間を埋めるためにgqlgencというツールを作って、弊社の開発フローを保とうと思いました。これがあると、スキーマクエリドリブンで開発が行えるようになります。

具体的には、スキーマを編集してgqlgenコマンドを叩くと、サーバーのコードが生成されます。そのあとサーバーに独自のロジックをどんどん追加して開発ができます。

ただ、例えばスキーマの変更が入ったり、新しいクエリを追加したりする時は、まずクエリを編集する必要があります。そのあとに、それに該当するクライアントStructやクライアントそのもののコードに変更を加えて、それを使って処理を書いていきます。

クエリ編集とクライアントの中間の部分をgqlgencに任せて、スキーマの変更、もしくは新しいクエリの追加であればクエリを変更してgqlgencコマンドを叩くことで、そのクライアントに該当するStructの型が変更されたり、クライアントそのものが変更されて生成されたりするので、あとはそれを使うだけです。

GraphQLを作成するうえで最低限必要な6つの条件

ここからは、GraphQL Clientの作成について話していきたいと思います。GraphQLを作成するにあたって、最低限何がそろっているとよいのか、6つの条件を挙げました。これはあくまでも最低限で、ほかにもGraphQLのAPIクライアントとして必要なものはあるのですが、今回は6つ挙げました。

それぞれを細かく見ていきます。まずは、GraphQL Schema Parserです。これはGraphQLスキーマを静的解析して、サーバー側で定義されているスキーマファイルを読み取って、そこからASTのツリーを作成するというものです。

次にIntorospection to Client Schemaです。Intorospectionという言葉があまり耳馴染みがない方も多いと思うのですが、これはGraphQLが用意している特殊なクエリで、GraphQLのサーバーに投げるとスキーマの定義の情報がJSON形式で取得できます。

このJSONからスキーマのASTツリーを作成することが可能になります。これがあると、スキーマファイルが手元になかったとしてもエンドポイントさえわかっていればスキーマ情報を取得できます。

次にGraphQL Query Parserです。これはクライアント側が書いているGraphQLのクエリを静的解析してASTのツリーを作るというものです。

4つ目が、GraphQL Query Response Model Ganeratorです。これはスキーマとクエリのASTからGraphQLの型を生成するものです。これまで用意してきたASTのツリーを使って、Goの型などを生成します。

次にGraphQL Client Generatorです。これはGraphQL Query Response Model Ganeratorで生成した型を使ってリクエストを投げて、返ってきた結果をレスポンスにマッピングできるクライアントを生成するというものです。

最後にGraphQL JSON Parserです。これはGraphQL特有のJSONをパースして、生成された型にマッピングするのですが、これはクライアントの中で使われるものなのですが、GraphQL特有の定義に対応していて、生成された型に対してマッピングができるJSON Parserを用意する必要があります。

次に、従来のGo GraphQL Clientが先ほどの6つのうちのどれに対応しているかを見ていこうと思います。

これは一番下のGraphQL JSON Parserのみに対応しています。なので、スキーマ情報を取ってきたり、Intorospectionを叩いたりはできません。単純にクエリのストリングを受けて、自分たちの手元で用意したレスポンスを受ける型のStructを用意して、それにデータをアサインしてくれるというかたちだったので、今回自分たちのユースケースには合わないと判断して採用はしませんでした。

次は、それぞれ6つの条件に当てはまるライブラリが存在しないのかを見ていこうと思います。まずはGraphQL Schema Parserです。これはgqlgenの作者が作っているgqlparserが存在するので、これを利用することにしました。

次にIntorospection to Client Schemaですが、これに関しては実装しているものが一切見つからなかったので、自作することにしました。次のGraphQL Query Parserですが、これはGraphQL Schema Parserと同じgqlparserが利用できるので、これを利用します。

GraphQL Query Response Model Ganeratorは、gqlgenがスキーマ情報からモデルのみを生成するものを作っているので、それを利用します。リクエスト、レスポンスの型に関しては生成するものがなかったので、それに関しては自作する必要があります。

次にGraphQL Client Generatorです。リクエスト、レスポンスを生成するものがない時点で、これも生成するものがないので作る必要があります。最後にGraphQL JSON Parserですが、これは先ほど紹介したクライアントのうちの1つ目のParserを参考に実装することにしました。

作成が必要なものは、Intorospection to Client SchemaとGraphQL Query Response Model GaneratorとGraphQL Client Generatorの3つで、そのうえでgqlparserやほかの実装を参考にしてgqlgencは作成されています。

(次回へつづく)