# HOW TO GRAPHQLでGraphQLの勉強をはじめました その①
写経しながら勉強したいなってことでAmazonでそれっぽい本を物色してたけど、やっぱりこの分野は変化が激しいっぽくて、翻訳された本よりはオンラインの英語の何かが良さそうかなってことで、The Fullstack Tutorial for GraphQLで勉強することにしました。
# Introduction
GraphQLとは何か?的な話から、Facebookによって開発されたOSSだけど、今では巨大なコミュニティによって〜とか、って話がありつつ、クライアント側からみたら一つのシングルエンドポイントでーとかってベーシックの説明があり、サーバー側はプロキシっていうか諸々吸収する感じで。クライアントアプリケーションの開発をシンプルにして加速させていきましょう、と。
GraphQLの歴史を振り返ると2012年からFacebookで使ってる、と。はじめてこのテクノロジーのことをFacebookが紹介したのは2015年のReactのカンファレンスだった。元々はReactだけで使われてたけど、今では様々なところで使われてるよ、と。
次の章では具体的になぜGraphQLがRestよりイイのか?ってところをやっていきます。
ちなみに次の章に進むにはクイズがあって、それに正解すると〜って感じになっています。
# GraphQL is the better REST
今日ではAPIと言えば、基本的にはRest APIを指す。GraphQLはよりフレキシブルで効率的なやり方。 例えばブログサイトがあったとして、名前 / 投稿 / フォロアーを表示するとする→3つAPIエンドポイントを呼ぶ必要がある。
GETで /users/id。まずはユーザーに関するデータを取得する。その際、名前だけ必要なのに、それ以外のアドレスや誕生日も取れてしまうかも。あんまり効率的じゃない。
GETで /users/id/posts。全部の投稿を返すとたくさんの情報をダウンロードすることになってしまう。タイトルだけでイイのに。
GETで /users/id/followers。最近の3フォロワーだけ必要かもしれないのに全部、、
この辺をGraphQLはどうやって解決しているかというと。まずエンドポイントは1つ。POSTリクエストでユーザーIDを指定して、必要な属性である名前だけ指定する。投稿からもタイトルだけ。フォロワーは最新の3件。👇のような感じ。
query {
User(id: "hogehoge1234") {
name
posts {
title
}
followers(last: 3) {
name
}
}
}
レスポンスは👇のようになる。最上位のノードはdata。そこから返ってきた情報。
{
"data": {
"User": {
"name": "Mary",
"posts": [
{ "title": "Learn Graphql Today" },
{ "title": "Title Hoge" },
{ "title": "Foo Title" },
{ "title": "graphql in a bar" },
],
"followers": [
{ "name": "Sido" },
{ "name": "Nancy" },
{ "name": "Yoshida Red" },
]
}
}
}
No more Over- and Underfetching 👉 overfetching: 要らん情報をダウンロード 👉 underfetching: n+1リクエスト問題(何個もエンドポイントをcallしなきゃいけない)
フロントエンドのUIを変えてデータのニーズが変わる度にRest APIのストラクチャを変更するのは現実的ではない。GraphQLならFine-grainedな情報をクライアントに返すことが出来るし、パフォーマンスのモニタリングにも良い。
GraphQLは強いスキーマと型が特徴。一旦決めてしまえばフロント側とサーバー側はそれぞれ開発を進めることが可能。
# Core Concepts
# The Schema Definition Language (SDL)
例えば👇の場合はPersonの属性は文字列型のnameと数値型のage。ビックリマークもしくはエクスクラメーションマーク)
type Person {
name: String!
age: Int!
}
それでもって、Postというブログの投稿に関しては文字列型のtitleがあったとします、と。
type Post {
title: String!
}
で、PersonとPostのリレーションは下記のように。Postに author を追加してそれが Person 型だぜとしてやって、Personの方は posts を追加して、それが[]ということでリストなので複数持てます、と。
type Person {
name: String!
age: Int!
**posts: [Post!]!**
}
type Post {
title: String!
**author: Person!**
}
# Fetching Data with Query
例えばユーザーの名前だけ欲しい時のリクエストは
{
allPersons {
name
}
}
レスポンスは👇のように名前だけ返ってくる。例えば年齢とかがデータベースにあったとしても余計なものは持ってこない。
{
"data": {
"allPersons": [
{"name": "Johnny"},
{"name": "Yoshida"},
{"name": "Red"}
]
}
}
ただ、仕様変更で年齢を取得しなければならなかったとしたら、それをリクエストで明示する。
{
allPersons {
name
**age**
}
}
そうすると年齢も返ってくるようになりますよ、と。
{
"data": {
"allPersons": [
{"name": "Johnny", "age": 11},
{"name": "Yoshida", "age": 21},
{"name": "Red", "age": 31}
]
}
}
そして、全てのユーザーを返すのではなくて直近の2人だけで良いとしたら、その条件をリクエストに加える
{
allPersons **(last: 2)** {
name
age
}
}
すると返ってくるのは2件になる。
{
"data": {
"allPersons": [
{"name": "Johnny", "age": 11},
{"name": "Yoshida", "age": 21}
]
}
}
GraphQLではクエリでネストを表現することが出来る。例えばユーザーが投稿したポストのタイトルを取得する場合
{
allPersons {
name
posts {
title
}
}
}
そうするとネストした結果が返ってきて、名前とそれぞれの投稿のタイトルが取得できます。
{
"data": {
"allPersons": [
{
"name": "Johnny",
"posts": [
{"title": "foo title 01"},
{"title": "bar title 02"}
]
},
{
"name": "Yoshida",
"posts": [
{"title": "hoge title 01"}
]
},
{
"name": "Red",
"posts": []
},
]
}
}
# Writing Data with Mutations
GraphQLでは書き込みはMutationで行われる。Mutationには3種類ある。
- creating new data
- updating existing data
- deleting existing data
シンタックスは基本的にはQueryと同様。👇こんな感じでリクエストすると
mutation {
createPerson(name: "Bob", age: 36) {
name
age
}
}
レスポンスは👇こんな感じ。
{
"data": {
"createPerson": {
"name": "Bob",
"age": 36,
}
}
}
GraphQLではID型を定義してサーバー側で自動的にユニークなIDを採番してくれる機構がある。👇のように定義。
type Person {
**id: ID!**
name: String!
age: Int!
}
生成されたIDだけをレスポンスで取得することも出来る。その場合のリクエストは
mutation {
createPerson(name: "Alice", age: 36) {
id
}
}
レスポンスでサーバー側で生成されたIDを取得することが出来る
{
"data": {
"createPerson": {
"id": "ck79wkp4l04ia0135810jt9zf"
}
}
}
# Realtime Updates with Subscriptions
Subscriptionでサーバーと接続を維持することができる。例えば以下のようにしておくことでサーバー側で新しいユーザーが追加された場合にそれを即座に取得することが可能。
subscription {
newPerson {
name
age
}
}
サーバー側で行われた変更はサーバー側からクライアントにPushされる。 (動画だと👇ようなブロックがブワッと連続で右から左に移動していくアニメーションになってる)
{
"newPerson": {
"name": "Jane",
"age": 23
}
}
# The GraphQL Schame
Query, Mutation, Subscriptionについて理解出来たところでGraphQLのスキーマについてのまとめ。
スキーマはクライアントとサーバーの取り決めである。でもって type Query { ... }, type Mutation { ... }, type Subscription { ... } のような Root Typeがある。
Mutationでは create だけ紹介してきたけど、updateとdeleteについては以下のように。IDを活用して〜、と。
type Mutation {
createPerson(name: String!, age:String!): Person!
**updatePerson(id: ID!, name: String!, age:String!): Person!**
**deletePerson(id: ID!): Person!**
}
PersonだけじゃなくてPostも必要なので〜
type Mutation {
createPerson(name: String!, age:String!): Person!
updatePerson(id: ID!, name: String!, age:String!): Person!**
deletePerson(id: ID!): Person!**
**createPost(title: String!): Post!**
**updatePost(id: ID!, title: String!): Post!**
**deletePost(id: ID!): Post!**
}
SubscriptionもnewPersonだけでなく。
type Subscription {
newPerson: Person!
updatedPerson: Person!
deletedPerson: Person!
newPost: Post!
updatedPost: Post!
deletedPost: Post!
}
# Big Picture (Architecture)
GraphQLはSpecification(仕様)でしかなくて、どのようなアクセスが受け付けられて、どのようなレスポンスを返すのか、というようなもの。プロジェクトで使う時はGraphQLサーバーを自分で立てるのよ、と。
GraphQLサーバーがデータベースと接続 Greenfield(新規のってイメージですかね)なプロジェクトで採用されることが多いGraphQLを実装したWebサーバーを使う。GraphQLはトランスポートレイヤーのどのプロトコルにも依存しないので、TCPでもWebsocketでも大丈夫。また、データベースやフォーマットも問いません。SQLなAWSのAuroraでもMongoDBでもOK。
GraphQLサーバーを既にあるシステムと統合する 既存に様々なAPIを提供する資産があるような場合。GraphQLのインターフェースは既存システムを統合したものとなり、複雑なデータ取得ロジックを隠蔽できる。ここでもデータベースやフォーマットは問わず、且つ、3rdパーティーのAPIを呼び出したり、なんていうものにも対応する。(んま、このGraphQLサーバーを構築したりメンテしたりする人は大変そうだよね…笑)
上記1.と2.のハイブリッド より柔軟性が高いというか、3rdパーティーのAPI呼びつつレガシーなシステムからデータを取ってきてガッチャンコしたり。
Front-end開発者はサーバーサイドのデータ取得の複雑性を考慮する必要がなく、どこから来たデータなのかもケアする必要はない。新しい抽象化の機会である。
Rest APIはImperative(HTTP接続してリクエスト送って、レスポンスパースして、ローカルで保持してからUI表示)だけど、GraphQLはDeclarative(必要なデータを記述してUI表示するだけ)。