graphql-goの入門記事が全く出てこなくて苦しみ悶えながらようやく理解できたので、ここに戦いの記録を残しておこうと思います。
今回の環境
今回はMongoDB+GraphQLという構成にしました。
成果物をここにまとめましたので、参照ください。 github.com
GraphQLとは?
そもそもGraphQLとはナニカ、ですがGraphQLはクエリ言語で、APIサーバーで使われます。
たとえばつぎのようなリクエストを送ってみます。
query { shop(name: "ラビットハウス") { name members { name } } }
すると以下のようなデータが返ってきます。
{ "data": { "shop": { "name": "ラビットハウス", "members": [ {"age":16,"name":"保登心愛"}, {"age":14,"name":"香風智乃"}, {"age":17,"name":"天々座理世"} ]} } }
GraphQLのメリット
上のクエリでは、shopフィールドにmembersフィールドが含まれているように書かれています。しかし、データベース上では実際に含んでいなくても構いません。そのため、RDBの面倒な結合についてクエリ上で考えずに済みます。
また、返ってくるデータの型がクエリに書いたとおりなので、とてもわかりやすいです。
処理の流れ
おおまかにGraphQLで上のクエリがどのように解決されるか見ていきます。
- shopフィールドを解決する
- リゾルバーがshopコレクションのなかでnameが"ラビットハウス"のものを返す
- shopフィールドが受け取ったフィールド(nameとmembers)について解決していく
- nameフィールドはデータにあるのでそのまま返す
- membersフィールドはshopドキュメントに存在しないので、membersフィールドのリゾルバーが動く
- membersフィールドのリゾルバーがmemberListを参照にmembersの配列を返す
ファイル構成
GitHub - pandanoir/graphql-gochiusa: GraphQL sample ココをもとに解説します。まず、各ファイルについてです。
- type/type.go データベースのドキュメントのフィールドを記述します。
- query/query.go GraphQLクエリでのフィールドを指定します。リゾルバーもここに書きます。
- main.go webサーバーを立ち上げ、受け取ったリクエストをgraphql-goに渡して結果を返します。
重要なのは、「データベースのドキュメントのフィールド」と「GraphQLクエリでのフィールド」が異なる点です。これにより、GraphQLでは結合して返されるフィールドが、データベース上では分割されていても問題がありません。
サンプルプログラムのドキュメントのフィールド
サンプルとして、以下のようなデータベースをつくりました。
shopコレクション
- id 店のID
- nameフィールド 店名
- memberListフィールド 所属するメンバーのIDを指定する
membersコレクション
- id メンバーのID
- name 名前
- age 年齢
MongoDBならshopのなかに直接membersを含むこともできますが、今回は分割しました。
サンプルプログラムのGraphQLクエリでのフィールド
query { shop { id name members { id name age } } }
このようになっております。クエリ上では、あたかもshopにmembersが含まれているかのように見えます。
resolverの書き方
まず、shopフィールドのリゾルバーを書きます。リゾルバーの返り値はデータベースから取得するドキュメントをそのまま指定します。そのため、返り値にはmembersは含まれません。
クエリはフィールドを参照すると、そのフィールドのリゾルバーを参照します。もし、そのフィールドがオブジェクトなら(shopやmembersフィールド)、さらにその中のフィールドを参照していきます。
idフィールドやnameフィールドはStringフィールドが指定されており、リゾルバーは指定していません。リゾルバーが指定されていない場合はshopフィールドリゾルバーが返した値=データベースのドキュメントの同名フィールドを参照します。
membersフィールドはリゾルバーが指定されています。このリゾルバー内でshopのmemberListに基づいて、membersの配列を返しています。仮にmembersフィールドがなかった場合、membersコレクションを参照しませんので、ムダが発生しません。