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
カスタムしながら理解するGraphQL Connection
Search
yana-gi
October 25, 2024
Programming
1
1.5k
カスタムしながら理解するGraphQL Connection
Kaigi on Rails 2024 (2024.10.25)
yana-gi
October 25, 2024
Tweet
Share
More Decks by yana-gi
See All by yana-gi
入社して1ヶ月 なんとかやってる話
yanagii
1
390
fjordbootcamp-200123
yanagii
1
580
Other Decks in Programming
See All in Programming
AWS IaCの注目アップデート 2024年10月版
konokenj
3
3.3k
GitHub Actionsのキャッシュと手を挙げることの大切さとそれに必要なこと
satoshi256kbyte
5
430
Tauriでネイティブアプリを作りたい
tsucchinoko
0
370
Enabling DevOps and Team Topologies Through Architecture: Architecting for Fast Flow
cer
PRO
0
320
Creating a Free Video Ad Network on the Edge
mizoguchicoji
0
120
リアーキテクチャxDDD 1年間の取り組みと進化
hsawaji
1
220
Compose 1.7のTextFieldはPOBox Plusで日本語変換できない
tomoya0x00
0
190
Flutterを言い訳にしない!アプリの使い心地改善テクニック5選🔥
kno3a87
1
160
Less waste, more joy, and a lot more green: How Quarkus makes Java better
hollycummins
0
100
ピラミッド、アイスクリームコーン、SMURF: 自動テストの最適バランスを求めて / Pyramid Ice-Cream-Cone and SMURF
twada
PRO
10
1.3k
最新TCAキャッチアップ
0si43
0
140
NSOutlineView何もわからん:( 前編 / I Don't Understand About NSOutlineView :( Pt. 1
usagimaru
0
330
Featured
See All Featured
Designing on Purpose - Digital PM Summit 2013
jponch
115
7k
Unsuck your backbone
ammeep
668
57k
The Cult of Friendly URLs
andyhume
78
6k
For a Future-Friendly Web
brad_frost
175
9.4k
Site-Speed That Sticks
csswizardry
0
23
Designing Experiences People Love
moore
138
23k
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
28
2k
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
16
2.1k
Testing 201, or: Great Expectations
jmmastey
38
7.1k
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
250
21k
What’s in a name? Adding method to the madness
productmarketing
PRO
22
3.1k
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
44
2.2k
Transcript
1 カスタムしながら理解する GraphQL Connection yana-gi Kaigi on Rails 2024 2024.10.25
2 ⾃⼰紹介 minne事業部 プロダクト開発チーム 2022年 中途⼊社 yana-gi やなぎ • 2021年
フィヨルドブートキャンプ卒業 • 2022年〜 GMOペパボ株式会社 Webアプリケーションエンジニア • GitHub : yana-gi • X : @yana_gis • ビールとお茶とみはしのあんみつが好き
3
4 • webの主な技術スタック ◦ Ruby on Rails ◦ Next.js ◦
MySQL • 段階的に移⾏中 ◦ フロント画⾯をRailsからNext.jsへ ◦ APIをREST APIからGraphQL APIへ • GraphQL APIの実装はgraphql-ruby minne
5 • WebはNext.jsに移⾏済み • GraphQL APIに移⾏済み この画⾯のAPIの実装の話👉 minneの検索画⾯
6 質問 🙋
7 GraphQLでAPIを 実装したことがある⽅ 🙋
8 GraphQLについて 初めて聞いた or 馴染みがない⽅ 🙋
9 1. GraphQLとは 1.1. minneでのGraphQL API使⽤例 1.2. minneで新規検索エンジンを導⼊する 1.3. Connection
Type 2. Custom Connection 2.1. クエリをしてからデータを返すまで 2.2. Custom Connectionの実装 2.3. 遅延評価 アジェンダ
GraphQLとは 10 クライアントが必要なデータだけを指定して取得できる データクエリ⾔語及びランタイム ™
None
Products Category Viewer Cart
GraphQLとは 13 エンドポイントで取得するデータを指定する • GET /api/products.json • GET /api/categories.json •
GET /api/cart.json • GET /api/viewer.json 1ページを表⽰するのに4回APIを実⾏する必要がある REST APIの場合
14 • 複数のデータ(ノード)を ⼀つのリクエストで取得する GraphQL APIの場合 GraphQLとは query productPage{ products
{ ... } categories { ... } cart { ... } viewer { ... } }
15 不要なデータのattributesまで取得してしまう Request : GET /api/products.json Response :👉 REST APIの場合
GraphQLとは
16 必要なデータ(ノード)の フィールドを指定できる GraphQL APIの場合 GraphQLとは query productPage { products
{ id name price } categories { id slug name } cart { ... } viewer { ... } }
17 • 既存の検索エンジン(Elasticsearch) • 新しい検索エンジン 新しい検索エンジンの導⼊ 🆕
18 • クライアントが利⽤するminne APIはGraphQL API ◦ クエリは既存の検索エンジンと同じように取得できるように ◦ 既存のクエリのページネーションは カーソルに加えてオフセットでも取得できる
• 新検索エンジンのAPIはREST API ◦ ページネーションはカーソルページネーションのみ 検索エンジン導⼊の要件
19 検索エンジン導⼊の要件
20 • minne側で実装するAPIはGraphQL API ◦ クエリは既存の検索エンジンと同じように取得できるように ◦ 既存のクエリのページネーションは カーソルに加えてオフセットでも取得できる •
新検索エンジンのAPIはREST API ◦ ページネーションはカーソルページネーションのみ 検索エンジン導⼊の要件
21 • オフセットページネーション • カーソルページネーション ページネーションの種類
• データの開始位置を指定する • offset と limit で取得する ◦ offset :
何件⽬から取得するか ◦ limit : 何件取得するか SQLの場合 オフセットページネーション 22 SELECT * FROM products LIMIT 10 OFFSET 30;
• 特定のデータポイント(カーソル)を基準に次のデー タを取得する • 時系列などの特定のデータをキーとして検索する ◦ cursor : ソート可能なデータポイント(カーソル) ◦
limit : 何件取得するか SQLの場合 カーソルページネーション 23 SELECT * FROM products WHERE id > 30 LIMIT 10;
改めて検索エンジンの導⼊要件を確認 24 ◯ オフセット ◯ カーソル ◯ オフセット ✗ カーソル
GraphQLでページネーションを簡単に実装できる GraphQL Connection 25
• Connection: ノード(データ)を リスト形式で取得する • Edge: ノード間の関係性や付加情報 • Node: 実際のデータ本体
Connection のノードの取得クエリ 26 query { products(first: 10) { # ProductConnection Type edges { # ProductEdge Type cursor node { # Product Type name id }, …
nodesからでも取得できる 27 Connection のノードの取得クエリ query { products(first: 10) { nodes
{ name id } } }
開始位置を指定する 28 Connection のノードの取得クエリ # abc以降のデータを10件取得する team { members(first: 10,
after: "abc") { nodes { id name } } }
29 Connection Typeの実装 field :items, Types::ItemType.connection_type, null: false def products
object.products # ActiveRecord Relationの場合 end fieldのTypeをconnection_typeに指定する サポートされているクラスの場合、要素を全て渡す
• 検索エンジンはREST APIで提供されている ◦ ページネーションはoffsetページネーションのみ • 結果を取得したデータを扱うクラスは ActiveRecordではない ◦ Connection
Typeでサポートされていないクラスになりそう ◦ サポートされていない場合は?🤔 改めて今回やりたいこと 30
Custom Connection 31
Custom Connectionを実装する Custom Connection Typeの実装 32 https://graphql-ruby.org/pagination/using_connections.html#return-collections def products #
ActiveRecordのリレーションを取得 relation = object.items # 作成したCustom Connection Typeのインスタンスを返す Connections::ProductsConnection.new(relation) end 参考: https://graphql-ruby.org/pagination/using_connections.html#return-collections
GraphQL::Pagination::Connection を継承したConnection Typeを作る 実装する必要があるメソッドが4つある Custom Connectionの実装 33 参考: https://graphql-ruby.org/pagination/custom_connections.html class
Connections::ProductsConnection < GraphQL::Pagination::Connection def nodes; ...; end # 返すべきノードの配列を取得 def has_next_page; ...; end # 次のページが存在するかを判定 def has_previous_page; ...; end # 前のページが存在するかを判定 def cursor_for(item); ...; end # 指定されたノードのカーソルを⽣成 end
• offset(開始位置)はどこで渡している? ◦ 検索エンジンAPIリクエスト時にoffsetを指定できない ⾃分が疑問に思ったこと 🤔 34
真相を知るために、クエリを実⾏してから値が返却される までを追う • シンプルなActive Recordの場合 • Active RecordかつConnectionの場合 クエリを実⾏してから値が返却されるまで 35
クエリを実⾏してから値が返却されるまで 単純にProductクラスを返す場合 36
クエリを実⾏してから値が返却されるまで Active RecordでConnectionを返す場合 1 37
クエリを実⾏してから値が返却されるまで Active RecordでConnectionを返す場合 2 38
クエリを実⾏してから値が返却されるまで Active RecordでConnectionを返す場合 3 39
クエリを実⾏してから値が返却されるまで Active RecordでConnectionを返す場合 4 40 offsetやlimitの計算
1. 疑問に思ったことの答え • Connection の offset (開始位置)はどうやって決まる? → Connection Class
でcursorから計算している 2. resolverを通った後にConnection Class で offset が決まる わかったこと💡 41
• resolverの時点ではoffsetとlimitが決まっていない • resolverの段階でAPI リクエストをするにもoffsetと limitが決まっていない さらなる疑問🤔 42
• Promiseクラスで遅延評価を⾏う ◦ ※ 既存の検索エンジンの実装を流⽤ 答え 43
遅延評価の流れ1 44
45 遅延評価の流れ2
46 遅延評価の流れ3
クエリをしてから値が返却されるまで1 47
クエリをしてから値が返却されるまで2 48
クエリをしてから値が返却されるまで3 49
クエリをしてから値が返却されるまで4 50
1. GraphQLでページングを⾏うにはConnectionを使う 2. 独⾃のモデルでConnectionを使うにはCustom Connectionを定義する 3. Connection Type Classでoffsetが決まる 4.
offsetを利⽤するためにresolverではPromiseクラスを利 ⽤して遅延評価を⾏う まとめ & 実装してみて分かったこと 51
• 1回のクエリで検索エンジン APIに複数回リクエストがされ ている 1. total_countやhas_next_pageなどを計算するタイミング 2. productsの値を取得するタイミング • total_countやhas_next_pageのみを取得するエンドポイン
トを作ってもらうなどの対応が必要そう 課題 52
• チームメンバー ◦ kazu(@kazuhitonakayam)さん ◦ saki(@Saki-htr)さん • 資料のレビュー ◦ daiki(@doew)さん
◦ pyamaさん • ⼤元のPromiseクラスやCustom Connection Typeの実装 ◦ ogidowさん special thanks 53
54 Thank you! よいGraphQLライフを!