43.3K Views
March 07, 21
スライド概要
iOSDC 2018で発表した資料です。
iOS × GraphQL の嬉しみとツラミ iOSDC 2018 @̲mogaming
About Me • Twitter: @̲mogaming • DeNAで小説系サービスの開発担当 - • iOS/Android App BFFやAPIの開発もしています 2
今日話すこと 1. GraphQL API クライアント編 2. GraphQL API サーバー編 3. GraphQL API iOSから叩く編 4. GraphQLの嬉しみとツラミ 5. BFFを活用してツラミに立ち向かってみた話 3
GraphQLとは APIサーバーに取り入れることのできるクエリ言語 • - RESTfulやgRPCとは全く違う思想でできている • - • Facebook作 https://graphql.org/ RESTful API GraphQL API gRPC SQLライクにAPIに問い合わせてデータを取得する 4
早速GraphQL APIを叩いてみよう! 今からGithubのGraphQL APIを題材にして 実際にAPIを叩く話をしていきます💪 https://developer.github.com/v4/explorer/ 5
と…その前に 少しだけ言葉の説明 " 6
リクエストの種類 • Query: RESTでいうとGET • Mutation: RESTでいうとPOST, PUT, DELETE GraphQLの特徴はQueryに集中しているためQueryの話をします 7
早速GraphQL APIを叩いてみよう! 今からGithubのGraphQL APIを題材にして 実際にAPIを叩く話をしていきます💪 https://developer.github.com/v4/explorer/ 8
開いてみるとこんな感じ なんかエディタみたいなのが開く…ナニコレ🤔? 9
GraphiQL(ぐらふぃくる?ぐらふぃかる?) • GraphiQLはブラウザで見れるGraphQL IDE • 型/Query/Mutationが一覧できる • Query/Mutationを書いて実行できる • 入力補完が効く • 導入はとっても簡単 https://github.com/graphql/graphiql 10
GraphiQLの使い方 RUN Documentが見れる ここに書く ここに結果が出る jsonで変数定義 11
実際に取得してみよう! Userの情報を取得(=Query)してみる💪 12
DocumentからUserを取得するQueryを探す ここを押す 13
userを取得してみる Queryを選択 Query一覧 userを検索 14
userを取得してみる user queryの説明 返される型 User Typeから取れるFields これを取ってみる Queryの引数 15
GraphQL APIの特徴 1.SQLライクにかける 2.必要なデータだけを1回で取得できる 3.interfaceに型がある 4.Paginationの仕様が決められている - ライブラリによっては実装されていなこともある 16
userを取得してみる SELECT bio FROM user WHERE login = ʻmogaming217ʼ みたいなイメージ 17
userを取得してみる 取得できた👏 18
GraphQL APIの特徴 1.SQLライクにかける 2.必要なデータだけを1回で取得できる 3.interfaceに型がある 4.Paginationの仕様が決められている - ライブラリによっては実装されていなこともある 19
必要なfieldだけ取得する avaterUrlも追加で取得する 20
複数のQueryを同時に投げる facebook/graphql リポジトリも取得してみる 21
複数のQueryを同時に投げる 複数APIにGETリクエストを送りがちな Top画面とかで活用できそう😊 22
GraphQL APIの特徴 1.SQLライクにかける 2.必要なデータだけを1回で取得できる 3.interfaceに型がある 4.Paginationの仕様が決められている - ライブラリによっては実装されていなこともある 23
型がしっかりある: Nullability Nullabilityが ! でわかる 24
型がしっかりある: Enum リポジトリに対する Permissionのfield Permissionとして返ってくる 値がEnumとして定義されている 25
型がしっかりある 実体としてはStringが返ってくる 26
型がしっかりある • あらゆるfieldに型を付けられる • 型に適合できていない場合は型システムがエラーを返す 27
GraphQL APIの特徴 1.SQLライクにかける 2.必要なデータだけを1回で取得できる 3.interfaceに型がある 4.Paginationの仕様が決められている - ライブラリによっては実装されていなこともある 28
Pagination • 例:ユーザーがスターしているリポジトリを取得する • たくさんあるかもしれないので全件取得はキビシイ • そういう場合のための Connection という型 - 今回の場合だと StarredRepositoryConnection 29
ConnectionType: Arguments 取得したい件数を指定するために argumentsとして first または last を必ず渡す必要がある 最初のn個 最後のn個 30
ConnectionType: Return Type } 1. 結果はedges.nodeに入ってきて nodeの位置はcursorで管理される } 2. よく使うであろう情報が PageInfoとして用意されている 31
ConnectionType: starredRepositoryを取得 starredRepositoryを取ってみる⭐ 最初の2件取得 pageの情報もとっておく 32
ConnectionType: starredRepositoryを取得 2件だけ取得できた🌟 この位置を指している 33
ConnectionType: Cursor cursorを after や before に渡せば そこを基準にしてfirst/lastを使うことができる = Pagination 34
ConnectionType: Cursorを指定する 先程のendCursorをafterに渡してみる 35
ConnectionType: Cursorを指定する できてる🎉 36
GraphQL APIの特徴 1.SQLライクにかける 2.必要なデータだけを1回で取得できる 3.interfaceに型がある 4.Paginationの仕様が決められている - ライブラリによっては実装されていなこともある 4つの特徴とどうやってGraphQL APIを 叩くのかをご理解いただけたでしょうか? 37
GraphQL サーバーサイド編 GraphQL APIサーバーはどのように 実装すればよいのか🤔? 38
GraphQL サーバーサイド視点 graphql-ruby 1.8.4 の場合でお話させていただきます🙇 39
UserTypeの定義 UserType 返される型 40
UserTypeの定義 field名, 型, 説明, nullabilityを宣言していく 定義 と Resolver ConnectionTypeに渡された 引数に応じて勝手にLIMITと OFFSETをつけてくれる 41
GraphQL サーバーサイド視点 ConnectionTypeを返すときに ほとんどそれを意識する必要がなくて素晴らしい🙄 42
User queryの定義 user query 43
user queryの実装 Query名(User => userになる) 返す型 user queryの引数 このqueryで返す処理 44
GraphQL サーバーサイド視点 たったこれだけでuser queryが完成😭 想像以上に簡単ですよね☺? 45
iOSからはどう叩く? iOSから叩くときはどうするのか? 46
iOSからはどう叩く? POSTでRequest Bodyに下記を渡すだけ 47
生URLSessionで頑張る 48
生URLSessionで頑張る これはツラい 49
つらいので ライブラリの力を借りたい😫 50
GraphQLクライアントライブラリ Apollo iOS https://github.com/apollographql/apollo-ios 51
Apollo iOSを使う:下準備 Apollo iOSを使うためには準備が必要です 52
Apollo iOSを使う:下準備 コード生成をします 53
Apollo-iOSを使う: 下準備 すべての型/Query/Mutations GraphiQLで書いた 情報が書かれている Queryをファイルとして用意 schema.json of Github GraphQL findUser.gql Apollo CLI Generate Code! Swift Code 54
Apollo-iOSを使う: .gqlファイル Queryに名前をつける 55
Apollo-iOSを使う 型のある世界! 56
iOSからGraphQL APIを叩く Apollo iOSの力を借りれば TypeSafeにGraphQL APIを叩くことができる😊 57
GraphQL APIの特徴 = GraphQLの魅力 1.SQLライクにかける 2.必要なデータだけを1回で取得できる 3.interfaceに型がある 4.Paginationの仕様が決められている これらがそのままGraphQLの魅力になる💪 58
iOSアプリエンジニア的GraphQLの嬉しみ • interfaceに型がある • GraphiQLが最高 - 常に最新のドキュメントになっている いつでも試しにAPI叩けるしクエリ作れる 59
サーバーサイド視点では • ⭕ クライアント毎にAPI作りわけなくて良くて楽 • ❌ 画像等のファイルをデフォルトでは扱えない - ファイルを扱う箇所だけはRESTfulにして対処している (ライブラリを入れれば扱えなくはない) • ❌ N+1問題が発生しやすくて大変 • ❌ エンドポイントが単一(/api/graphql)のためCache難しい - いままでの知見があまり使えない 60
GraphQLのツラミ さて……ツラミの話をします 61
GraphQLのツラミ エラーハンドリングが難しい 62
エラーはどう来るのか エラーはこんな感じで来る 63
GraphQLのツラミ:エラーハンドリング • 複数のQuery/Mutationを同時に叩くと一部成功して 一部失敗とかが起こりうる • どれがクリティカルなものなのかの判断が必要 いつもどおり頑張って考えてやるしかない💪 64
GraphQLのツラミ statusCodeが基本200で返ってくる 65
GraphQLのツラミ:statusCode • statusCodeは基本200で返ってくる • GraphQLはリクエストが通れば200 • apollo-iosからはstatusCodeが見えなくなっている • Mutation(更新系)が特にツラい 66
Mutationに対する超個人的見解 • GraphQLはQueryがとても魅力的だがMutationはそれほど • 更新系はGraphQLであるありがたみがほとんどない 67
GraphQLのツラミ このツラミをなんとか解決できないかな…🤔? 68
statusCodeが200問題 まずGraphQL API側でエラーの内容を拡張する 69
API側でエラーを拡張する GraphQL API側でエラーを拡張して statusCodeをレスポンスに付与する 70
アプリ側でstatusCodeを参照する statusCodeを取り出す 71
statusCodeが200問題 なんとかなってる気がする…🤔? 72
しかしワイは思った 「いやワイは、Mutationの結果は 普通にStatusCodeでハンドリングしたいんじゃー😡」 73
しかしワイは思った なぜかというと 74
Backends For Frontends 弊サービスはMicroservices構成 iOS App BFF Payment Image/Movie Core Domain Notification RESTful RESTful GraphQL RESTful そもそもRESTful APIも叩かないといけないことがある 75
BFFを用いた黒魔術 iOSからみてMutation(更新系)だけRESTっぽく 振る舞わせることもできます🧙 76
BFFを用いた黒魔術 ① URLSession等で POST: /api/createNovel iOS App ⑥ HTTP StatusCode でハンドリング ② GraphQLの Mutationに マッピング ③ Mutation createNovel() BFF ⑤ errors.first.statusCodeを HTTP StatusCodeとする GraphQL API ④ “errors”: [{ “statusCode”: 400 }] 77
BFFを用いた黒魔術 できました☺ 78
BFFを用いた黒魔術 こういうふうにもできるよっていうだけなので オススメはしません2 もしかしたらこれがイイってなることもあるかも…? 79
まとめ GraphQLはなかなかいいぞ • - interfaceに型があるの最高 GraphiQL最高 GraphQLはエラーハンドリングが結構むずかしいぞ • - BestPracticeがあまりなくとても悩ましい 懇親会や弊社DeNAブースでもいつでもお声がけください! 80
参考資料 81
Apollo-iOSを使う: 下準備(詳細) apolloをnpm install • - npm i -g apollo apolloを使ってGithub GraphQL APIのschemaを取得 • - apollo schema:download --endpoint=https://api.github.com/graphq --header="Authorization: Bearer <token>" schema.json • GraphiQLで書いたqueryに名前つけて.gqlファイルとして保存 • schemaと.gqlからapolloを使ってコード生成 - apollo codegen:generate --queries=findUser.gql --schema=schema.json API.swift 82