読者です 読者をやめる 読者になる 読者になる

GraphQLを勉強した

自分でGraphQLサーバーを実装しながら勉強したログ。間違ってるかも。

コードはここにあるが、何の注釈もない。 https://github.com/mizchi-sandbox/play-graphql-server

RESTの課題

REST は URI とモデルのマッピング構造だが、往々にしてクライアントで必要となる構造は モデルのうち一部であったり、そのリレーショナルな構造に依存する。 つまり、REST というルールに従って必要なデータを組み立てると、リレーショナルな構造によってN回のリソースへのアクセスと、興味がないデータを含んだ不要なペイロードが発生しがちである。

GraphQL は何をしたいか

  • 1リクエスト内でモデルへの問い合わせを合成し、さらに必要なものだけ返却したい
  • 言語とは独立した、転送経路上のモデルの定義を行いたい

パフォーマンス上の理由とセマンティクスが同居しているが、不適当な要求ではない、と思う。

GraphQL の解決アプローチ

GraphQLサーバーは、まずモデルのプロパティと、他のモデルとの関係のグラフ構造を定義する。次に、クライアントに対して公開するクエリを定義する。

GraphQLクライアントは、使うクエリの選択と、その絞込みクエリの2つを同時に送信する。

クライアントからクエリを受けたGraphQLサーバーは、クエリ定義にそって、なんらかの方法で実装されたモデル抽出関数などを叩き、そこで得られたモデルを、定義にしたがって合成し、絞込クエリによってフィルタし、返却する。

URL的には、シングルエンドポイントを持ち、そこに向けてクエリを発行する。モデルの合成は1リクエスト内で行われる。RESTリソース的なセマンティクスは、GraphQLは興味がない。

GraphQLは何をしないか

  • バックエンドの実装
    • データの通り道のスキーマを定義しているだけ
    • なんらかの言語の実装によって自分で書く必要がある
    • 最終的なペイロードに載せるデータのフィルタリングはGraphQL側が行うが、その組立は自分でやる必要があるため、その過程にパフォーマンスの劣化は当然発生する。たとえばRubyバックエンドで ActiveRecordto_hash なんらかのシリアライズ(追記: ActiveRecord::Baseにto_hashはない、とのこと)で深いリレーショナルのプロパティを掘り出してしまったら、そのSQLコストは発生する。
  • データの転送経路の指定
    • HTTPでもWSでも生のソケット通信でもなんでも良いが、環境ごとに実装する必要がある
    • 極端に言えばローカルのIndexedDBへローカルのクライアントから問い合わせる、などもある

バックエンドはなんでもよい。MySQL, MongoDB, In Memory、DynamoDB、etc…。 実装言語と独立しているため、サーバークライアントともに、実装は何の言語でも良い。サーバーをElixirで実装してiOSのGraphQLクライアントから呼ぶ、というようなケースも全然あるだろう。

GraphQLのスキーマ

クエリは大きく2つに分類でき、QueryとMutationがある。スキーマ上の type Query {...}type Mutation {...} は特殊化されていて、それらの名前空間はクライアントの query {...}mutation {...} に対応し、いわゆるトップレベルスコープのような扱いになる。それら2つは明確に区別されている。が、実際にどう副作用が起こるかは実装依存なので、queryで副作用を加えることは、一応可能である。やらないとは思うが。

雑感

目的に対して妥当な実装ではあると思うが、資料が少なく、未だ学習コストが高い。その学習コストを乗り越えれば、クラサバ間の通信仕様として、有効かもしれない。

シングルエンドポイントというのが、AWS Lambda などとも相性がよく、DynamoDBバックエンドなどとも有効な組み合わせになる。

自分はFlowtypeで書いていたが、静的型付けな言語環境では、GraphQLスキーマとその言語上の型を2つ定義する必要があり、やや面倒ではあった。ジェネレータがあるといいかもしれない。Subtypingできる言語ならややマシかも。

と思って調べてたらFlowtypeではGraphqlを直接パースするというアプローチを実装中だった。ただ、いまいち何の型をみてるのかよくわからない。Relay以外で使えるのか。https://github.com/facebook/flow/pull/2822

Facebook graphql を直接使うより, Apollo のツールチェイン使ったほうが楽