Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
本番環境のRailsプロダクトでGraphQL API / GraphQL API on Ra...
Search
roolrool
July 04, 2019
Programming
1
1.5k
本番環境のRailsプロダクトでGraphQL API / GraphQL API on Rails Products in Production
roolrool
July 04, 2019
Tweet
Share
More Decks by roolrool
See All by roolrool
検索のMicroservices化 with Apollo Server
roolrool
2
1.5k
Other Decks in Programming
See All in Programming
リアーキテクチャxDDD 1年間の取り組みと進化
hsawaji
0
110
Why Spring Matters to Jakarta EE - and Vice Versa
ivargrimstad
0
1.1k
LLM生成文章の精度評価自動化とプロンプトチューニングの効率化について
layerx
PRO
2
140
外部システム連携先が10を超えるシステムでのアーキテクチャ設計・実装事例
kiwasaki
1
240
gopls を改造したら開発生産性が高まった
satorunooshie
8
260
Content Security Policy入門 セキュリティ設定と 違反レポートのはじめ方 / Introduction to Content Security Policy Getting Started with Security Configuration and Violation Reporting
uskey512
1
440
弊社の「意識チョット低いアーキテクチャ」10選
texmeijin
5
23k
Kaigi on Rails 2024 - Rails APIモードのためのシンプルで効果的なCSRF対策 / kaigionrails-2024-csrf
corocn
5
3.5k
役立つログに取り組もう
irof
27
8.9k
3rd party scriptでもReactを使いたい! Preact + Reactのハイブリッド開発
righttouch
PRO
1
200
『ドメイン駆動設計をはじめよう』のモデリングアプローチ
masuda220
PRO
8
460
cXML という電子商取引の トランザクションを支える プロトコルと向きあっている話
phigasui
3
2.3k
Featured
See All Featured
Automating Front-end Workflow
addyosmani
1365
200k
The Cult of Friendly URLs
andyhume
78
6k
What’s in a name? Adding method to the madness
productmarketing
PRO
22
3.1k
4 Signs Your Business is Dying
shpigford
180
21k
Building a Modern Day E-commerce SEO Strategy
aleyda
38
6.9k
Rails Girls Zürich Keynote
gr2m
93
13k
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
7
160
Music & Morning Musume
bryan
46
6.1k
Documentation Writing (for coders)
carmenintech
65
4.4k
Ruby is Unlike a Banana
tanoku
96
11k
Art, The Web, and Tiny UX
lynnandtonic
297
20k
GraphQLとの向き合い方2022年版
quramy
43
13k
Transcript
本番環境のRailsプロダクトで GraphQL APIを実装した話 2019/7/4 Ryosuke Yamamoto
2 自己紹介 Ryosuke Yamamoto ( @roolrool ) Webエンジニア 男子シンクロのインストラクター、 Webディレクターを経て2017年1
月にスペースマーケットにジョイン。 開発比率:バックエンド 5:5 フロントエンド
3 担当施策で直近の大きなリリース
4 スペースマーケットEVENTにチケット決済機能追加!
5 決済リリース前 参照系: GraphQL API / 更新系: REST API 決済リリース後
すべてGraphQL APIに移行 スペースマーケットEVENTのAPI構成
6 弊社のGraphQL APIの実装で 工夫しているポイントについて話します! 参考: RailsでGraphQL APIを作る時に悩んだ5つのこと
7 GraphQL
8 • クエリ言語とスキーマ言語からなるAPIのための規格 ◦ クエリ ▪ リクエスト用の言語 ▪ 更新系: Mutation
▪ 参照系: Query ◦ スキーマ ▪ データ型を定義 • 単一エンドポイント ◦ /graphql GraphQLとは
9 スキーマ クエリ レスポンス GraphQLの例 クエリをresolverで処理してレスポンスを返す
10 詳しくはgfxさんの記事をどうぞ
11 ※前提 APIのフレームワークがRailsなので、 graphql-rubyを使用してGraphQL APIを実装
12 話すこと • ページネーション • N+1問題 • 画像/ファイルアップロード • アクセス権限管理
13 ページネーション
14 • 標準のものはRelayのCursor Connectionというカーソルベースのページ ネーション • graphql-rubyもRelayの仕様をサポート ◦ 導入しやすい GraphQLのページネーション
15 カーソル・・?? (初めて知った瞬間の脳内 )
16 • レコードごとにユニークなID(カーソル)を割り当て、そのカーソルを起点として 前後のデータを取得する方式 • レコードの増減によるずれが起きない点がメリット カーソルベースのページネーション
17 カーソルベースのページネーション node (データそのもの) cursor (edgeごとのユニークなID) page(edges+pageInfo) edge ・・・・
18 件数 起点
19 • アプリケーションの仕様上、Offset&Limitページネーションが必要だった • カーソルベースはTwitterタイムラインのような無限スクロールコンテンツには向 いているがページ数を指定したい場合に向かない カーソルベースの問題点
20 ということで Offset&Limitページネーションを実装
21 • Railsのページネーションのデファクトと言えばkaminari ◦ ページネーション機能と関連メソッドを提供するgem ▪ 例) object.page(1).per(10).total_pages: 合計ページ数 Offset&Limitページネーションの実装
22 kaminariを利用して独自のpageInfoを定義
23 カーソルベース ページネーション比較 Offset&Limit
24 N+1問題
25 N+1問題 GraphQLは何も対策しなければサクッとN+1が発生する →resolverを再帰的に実行するのでその度にSQLが 発行されてしまうため
26 サクッとリクエスト N+1問題
27 サクッとN+1 N+1問題
28 N+1?eager loadingだ!(^q^)
29 • GraphQLに先読み(eager loading)は不向き ◦ 回避自体は可能 ◦ どのテーブルがひかれるかどうかはクエリ次第なので先読みしても使わ れない可能性がある ◦
associationのfieldを追加する度に先読みを追加することになる eager loadingで回避? SQLを遅延評価したい
30 • resolverの実行の度にSQLを解決せず、必要なデータを蓄積してから 最後にまとめて解決する • クエリに対して必要なSQLだけを発行するので無駄がない • javascriptならdataloader • Rubyならgraphql-batch
SQLの遅延評価
31 • resolverの実行の度にSQLを解決せず、必要なデータを蓄積してから 最後にまとめて解決する • クエリに対して必要なSQLだけを発行するので無駄がない • javascriptならdataloader • Rubyならgraphql-batch
SQLの遅延評価
32 graphql-batch
33 • Shopify製のライブラリ • 内部で非同期処理のgem(lgierth/promise.rb)を使用している • 単体ではActiveRecordに依存していないが、サンプルが用意されているので その通りに実装すればOK ◦ AssociationLoader
◦ RecordLoader graphql-batchとは
34 AssociationLoader
35 • has_manyな関連レコードを取得する際に使用 • オブジェクトの関連レコードをすべて返す AssociationLoaderとは
36 confidential AssociationLoader定義
37 AssociationLoader呼び出し user.events のイメージ
38 • 下記理由で使用せず ◦ 弊社では独自にページネーションを実装していたため関連をまるまる取 得する形では要件を満たせなかった ◦ resolver内で絞り込みを行った結果を返したかった AssociationLoaderは不採用
39 RecordLoader
40 • belongs_to or has_manyな関連レコードを取得する際に使用 • 指定したモデル名&渡したidのレコードを返す RecordLoaderとは
41 RecordLoader定義
42 RecordLoader呼び出し
43 • idを単体か配列で渡せば遅延評価してくれる ◦ 絞り込んだ結果のレコードセットのidを渡せばいいので柔軟 RecordLoaderを採用
44 • has_many: ◦ RecordLoader#load_many • belongs_to: ◦ RecordLoader#load •
has_one: × ※関連の外部キーを持たない場合 ◦ 良い方法を調査中 各関連の対応状況
45 画像/ファイルアップロード
46 • GraphQLサーバーへのデータの受け渡しはJSONが一般的 • JSONではバイナリデータを扱うことができない ◦ ファイルのアップロードには工夫が必要 GraphQL×ファイルアップロード
47 • multipart/form-dataで送信 ◦ htmlのform submitやjsのFormDataオブジェクトを使用 • Base64にエンコードしてJSONで送信 アップロード方法の選択 Base64にエンコードする方法を採用
48 Base64に対応したアップローダー
49 バックエンドの実装
50 フロントエンドの実装
51 • データサイズが大きくなる ◦ エンコード前と比較して33%増加 • オンメモリで扱うには巨大すぎるデータになってしまう可能性 ◦ リクエストボディのサイズ制限は合わせて必要 Base64の問題点
52 アクセス権限管理 confidential
53 • fieldやObjectTypeそのものに対してアクセス制限を入れたい • ログインユーザーの個人情報や売上データ等 ◦ 誰でも引けると困るデータ ◦ 権限チェックをresolver毎に書くのはつらい アクセス権限管理の目的
54 一括で権限を設定できるライブラリを利用
55 アクセス権限の定義
56 schema定義時に宣言
57 所感など confidential
58 移行してみて • good ◦ ページに対して必要なデータだけを取得するという柔軟なリクエストが出 来るのはやはり良い ◦ 加工したデータを返すfieldなどRESTではレスポンスサイズを気にして持 たせづらかったものも気軽に追加できるようになった
• bad ◦ パフォーマンス分析のツールが揃っていない ▪ Apollo ServerならApollo Engine ▪ New Relic • クライアント側でquery名をユニークにする必要あり
None