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
2
3k
カスタムしながら理解する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
420
fjordbootcamp-200123
yanagii
1
660
Other Decks in Programming
See All in Programming
Design Foundational Data Engineering Observability
sucitw
3
160
Testing Trophyは叫ばない
toms74209200
0
650
詳解!defer panic recover のしくみ / Understanding defer, panic, and recover
convto
0
210
More Approvers for Greater OSS and Japan Community
tkikuc
1
110
Kiroで始めるAI-DLC
kaonash
2
540
Azure SRE Agentで運用は楽になるのか?
kkamegawa
0
1.4k
AI時代のUIはどこへ行く?
yusukebe
13
7.7k
Flutter with Dart MCP: All You Need - 박제창 2025 I/O Extended Busan
itsmedreamwalker
0
130
速いWebフレームワークを作る
yusukebe
5
1.7k
Zendeskのチケットを Amazon Bedrockで 解析した
ryokosuge
3
270
もうちょっといいRubyプロファイラを作りたい (2025)
osyoyu
0
240
FindyにおけるTakumi活用と脆弱性管理のこれから
rvirus0817
0
410
Featured
See All Featured
Scaling GitHub
holman
463
140k
Writing Fast Ruby
sferik
628
62k
Being A Developer After 40
akosma
90
590k
jQuery: Nuts, Bolts and Bling
dougneiner
64
7.9k
Building Flexible Design Systems
yeseniaperezcruz
328
39k
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
53
2.9k
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
234
17k
Optimising Largest Contentful Paint
csswizardry
37
3.4k
Speed Design
sergeychernyshev
32
1.1k
The Straight Up "How To Draw Better" Workshop
denniskardys
236
140k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
12
1.1k
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
111
20k
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ライフを!