Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
カスタムしながら理解するGraphQL Connection
Search
yana-gi
October 25, 2024
Programming
4.1k
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
カスタムしながら理解するGraphQL Connection
Kaigi on Rails 2024 (2024.10.25)
yana-gi
October 25, 2024
More Decks by yana-gi
See All by yana-gi
入社して1ヶ月 なんとかやってる話
yanagii
1
450
fjordbootcamp-200123
yanagii
1
720
Other Decks in Programming
See All in Programming
App Storeの外へ──日本のiOSサイドローディング入門 for iOSDC Japan 2026
yuukiw00w
0
210
市販E-Readerを乗っ取れ 〜Embedded Swiftで電子ペーパーガジェットを制御する〜
trickart
0
190
TiDB Cloudのカスタムコントローラーによるオートスケール対応
takaidohigasi
0
120
モバイル交通系ICへのチャージ実例から考える、クロスプラットフォーム開発におけるiOS実機テスト設計とCI運用
yusuga
1
470
海上で動くGoサーバー: goroutineとchannelでさばく航行データストリーム
atsuki_seo
0
690
App Intentsのビルドプロセスを支える技術
kntkymt
0
370
一人だけ、Kiroが静止する日
hideg
0
110
XHTMLが残したもの
yosuke_furukawa
PRO
2
800
Vibes Containers 〜AIで変わるコンテナ設計と運用〜
tkikuc
3
550
速習iPhone Duo対応
yuukiw00w
1
350
The Past, Present, and Future of Enterprise Java
ivargrimstad
0
370
20260828_品質と開発生産性を両立させる、AI時代のE2Eテストの考え方
magicpod
0
210
Featured
See All Featured
Build your cross-platform service in a week with App Engine
jlugia
234
19k
Designing for Timeless Needs
cassininazir
1
480
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
21
1.6k
Noah Learner - AI + Me: how we built a GSC Bulk Export data pipeline
techseoconnect
PRO
0
430
Building a Modern Day E-commerce SEO Strategy
aleyda
45
9.2k
Primal Persuasion: How to Engage the Brain for Learning That Lasts
tmiket
0
450
Chasing Engaging Ingredients in Design
codingconduct
0
310
Impact Scores and Hybrid Strategies: The future of link building
tamaranovitovic
0
440
A brief & incomplete history of UX Design for the World Wide Web: 1989–2019
jct
2
510
Git: the NoSQL Database
bkeepers
PRO
432
67k
Why You Should Never Use an ORM
jnunemaker
PRO
61
10k
Embracing the Ebb and Flow
colly
88
5.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ライフを!