ushumpei’s blog

生活で気になったことを随時調べて書いていきます。

GraphQLに関するメモ

GraphQLについて調べたことのメモです。

GraphQLはFacebookによって考案・開発されたクライアントアプリ向けのクエリ記述言語です(仕様)。モバイルアプリ向けの使いやすいAPIを実現するために考えられていて、2015年にオープンソースになって騒がれ始めました。Facebook, GitHub, Pinterest…など大規模な方々が使っているそうです。

思想としてはRESTにかけている柔軟性をAPIに持たせるために作られていて、 単一のエンドポイントから必要なデータを必要な分、明示的に取ってくることができるようになっています。うまくやればデータ通信量を減らせる上にいろいろメリットがあったりします。

概要

仕様によるとGraphQL以下のコンセプトでデザインされているそうです。私の意訳、ひどいので感じを掴むくらいで読んでください。また何かいいものがあれば教えていただきたいです。

  • 階層型データ構造(Hierarchical):
    • クエリ自体が階層を持っているので、レスポンスと構造が一致して理解しやすいし、検索条件なども記述しやすい。
  • 製品中心(Product‐centric):
    • フロントエンドとフロントエンドエンジニアファースト。
  • 強い型付け(Strong-typing)
    • クエリが定義された型によって安全に実行されることが保障されます。
  • クライアント主導の問い合わせ(Client‐specified queries)
    • 従来のAPIはサーバー側が何を送るか決めていましたが、クライアント側がフィールド単位で何が欲しいかで問い合わせることができます。
  • 内省的(Introspective)
    • GraphQLサーバーはGraphQLで問い合わせできるようになっている(サーバーに型定義を問い合わせることができるので、その意味かと思います?)

GraphQLのクエリとレスポンスのイメージ

例えばブログサイトのAPIとかでユーザーの記事のタイトルを一覧表示しようと思った場合、RESTだとユーザーに紐付く記事一覧をGETするエンドポイント(/users/:id/postsとか、または/posts?user=id)を作って、そこを叩いて返ってきたレスポンスをクライアント側でフィルタリングするかと思いますが、GraphQLだと単一のエンドポイントで記事名の一覧のみを取得できるクエリがかけます。

query {
  user(id: "1") {
    name
    posts {
      title
    }
  }
}

上のクエリをクライアントから発行すると、クエリとそっくりな構造の以下のレスポンスが返ってきます。

{
  "data": {
    "user": {
      "name": "ushumpei",
      "posts": [
        { title: "GraphQLの概要" },
        { title: "Relayで遊ぶ" }
      ]
    }
  }
}

キーワードの説明

サーバーにスキーマと呼ばれる形で、API定義を行います。以下のキーワードを用いて定義していきます。

  • 型定義
    • Type
    • Interface
  • 操作定義
    • Query
    • Mutation
      • Fragment (補助)
      • Input (補助)

Type

型の定義を行います。

type User {
  id: String
  name: String
  posts: [Post]
  ...
}

Interface

型から継承できるインタフェースも定義できます。フィールドとしてインタフェースの型を指定するような、以下のような使い方ができます。

interface Media {
  url: String
}

type Image impliments Media {
  url: String
  width: String
  ...
}

type Audio impliments Media {
  url: String
  playTime: Int
  ...
}

type Post {
  title: String
  media: Media // Image, Audio両方をフィールドに持てる。この場合インタフェースで定義されているurlのみ取得可能。
  ...
}

Query

受け付けるクエリを定義します。

type Query {
  user(id: String): User
  users: [User]
}

この定義に対しては以下のようなクエリをクライアントから発行できるイメージです。Userのどのフィールドを取ってくるとかは自由です。

query {
  user(id: "1") {
    id
    name
    profile
  }
}

Mutation

更新を受け付けるためのインタフェースを定義します。

type Mutation {
 createUserPost(userId: String!, title: String!, body: String!): Post
}

クライアントからは以下のような問い合わせが想定できます。

mutation {
  createUserPost(userId: "1", title: "GraphQLの概要", body: "GraphQLについて...") {
    id
    title
  }
}

(補助)Fragment

クライアント側からレスポンスを記述しやすくするために、複数のフィールドをひとかたまりに断片化してQuery、Mutationに記述できます。

fragment basicInfo on User {
  id
  name
  profile
}

query {
  user: {
    ...basicInfo
  }
}

(補助)Input

Query、Mutationに対して引数のセットを定義できます。先ほどのMutationは次のように書き直せます。(作成、更新とかで引数をまとめられるのと、type Mutationが読みやすくなると思います)

input PostInput {
  userId: String!
  title: String!
  body: String!
}

type Mutation {
 createUserPost(post: PostInput!): Post
}

とりあえず試すには?

チュートリアルを公開してくださっている記事があるのでやって見たら少し解りました。 ただしApolloClientのバージョンが2になりnetworkInterfacecustomResolversなど消えた機能があるのでバージョン気をつけてください、バージョン

参考: All you need to know about Apollo Client 2

問題

あくまでクエリ記述言語なので、どのようにデータを取得するかは自分で書く必要があります。データストレージとして何を採用するかなども考える余地しかない感じです。

とりあえずは、graphql-server-express(apollo-server-expressに変わった?)とかで練習できますが、データ保持はオンメモリしかやっていないです。

外部ホスティングサービスとしてはざっと検索すると、scaphold.io, graph.cool, meldioとかありますが、まだ使って見ていないのでなんとも言えません。

感想

GraphQL旨味としては クライアントがなんのデータを取得しているか楽に明示できることだと思います。クエリの形を見れば何を取ってきているかわかります。

RESTでも取得するフィールドをクエリパラメータで指定して取ってこれるようにすれば取得しているデータを明示できますが、検索条件とかも色々絡んでくるとクエリ自体がかなり長くなって脳に負担になってきます。

またその場合ドキュメンテーション必須です。それに比べるとGraphQLは共通認識化しやすそうですし、定義したスキーマがドキュメントの役割を果たせるので(実際、型定義の際にdescriptionフィールドにmarkdownドキュメンテーションできる)そこも楽になるかと思います。

雑記

  • ライブラリの名前変わったりいろいろモジュール化が盛んなのでもうちょっと頭整理したい
  • GraphQL運用で使って見たスライドとか調べるとある
  • データ永続化はPostGraphQLとかどうなんでしょうか
  • NoSQLでのうまい使い方とか誰か考え済みなんでしょうか。NoSQLのデータ設計方法が全然わかっていないので一度どこかでちゃんと体験しないといけない。
  • 「フィリピン -> ネットインフラ弱い -> でもネット高い -> でもFacebookアクセス無料 -> みんなめっちゃ使う」の裏でGraphQLが活躍しているのかもしれない