Lock in $30 Savings on PRO—Offer Ends Soon! ⏳
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.7k
本番環境の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.7k
Other Decks in Programming
See All in Programming
S3 VectorsとStrands Agentsを利用したAgentic RAGシステムの構築
tosuri13
6
300
【Streamlit x Snowflake】データ基盤からアプリ開発・AI活用まで、すべてをSnowflake内で実現
ayumu_yamaguchi
1
120
SwiftUIで本格音ゲー実装してみた
hypebeans
0
160
Full-Cycle Reactivity in Angular: SignalStore mit Signal Forms und Resources
manfredsteyer
PRO
0
120
MAP, Jigsaw, Code Golf 振り返り会 by 関東Kaggler会|Jigsaw 15th Solution
hasibirok0
0
230
LLM Çağında Backend Olmak: 10 Milyon Prompt'u Milisaniyede Sorgulamak
selcukusta
0
120
令和最新版Android Studioで化石デバイス向けアプリを作る
arkw
0
390
関数実行の裏側では何が起きているのか?
minop1205
1
690
新卒エンジニアのプルリクエスト with AI駆動
fukunaga2025
0
200
Why Kotlin? 電子カルテを Kotlin で開発する理由 / Why Kotlin? at Henry
agatan
2
7k
AtCoder Conference 2025「LLM時代のAHC」
imjk
2
400
Developing static sites with Ruby
okuramasafumi
0
260
Featured
See All Featured
Rebuilding a faster, lazier Slack
samanthasiow
84
9.3k
We Have a Design System, Now What?
morganepeng
54
7.9k
Unsuck your backbone
ammeep
671
58k
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
46
2.6k
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
16
1.8k
[SF Ruby Conf 2025] Rails X
palkan
0
500
How to train your dragon (web standard)
notwaldorf
97
6.4k
Code Reviewing Like a Champion
maltzj
527
40k
Building Adaptive Systems
keathley
44
2.9k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
27k
jQuery: Nuts, Bolts and Bling
dougneiner
65
8.2k
Git: the NoSQL Database
bkeepers
PRO
432
66k
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