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
1.9k
カスタムしながら理解する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
590
Other Decks in Programming
See All in Programming
rails statsで大解剖 🔍 “B/43流” のRailsの育て方を歴史とともに振り返ります
shoheimitani
2
940
たのしいparse.y
ydah
3
120
Mermaid x AST x 生成AI = コードとドキュメントの完全同期への道
shibuyamizuho
0
160
[JAWS-UG横浜 #76] イケてるアップデートを宇宙いち早く紹介するよ!
maroon1st
0
480
Effective Signals in Angular 19+: Rules and Helpers
manfredsteyer
PRO
0
110
선언형 UI에서의 상태관리
l2hyunwoo
0
180
Monixと常駐プログラムの勘どころ / Scalaわいわい勉強会 #4
stoneream
0
280
ある日突然あなたが管理しているサーバーにDDoSが来たらどうなるでしょう?知ってるようで何も知らなかったDDoS攻撃と対策 #phpcon.2024
akase244
1
150
Exploring: Partial and Independent Composables
blackbracken
0
100
ChatGPT とつくる PHP で OS 実装
memory1994
PRO
2
110
KMP와 kotlinx.rpc로 서버와 클라이언트 동기화
kwakeuijin
0
160
テストコード文化を0から作り、変化し続けた組織
kazatohiei
2
1.5k
Featured
See All Featured
No one is an island. Learnings from fostering a developers community.
thoeni
19
3k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
26
1.9k
Designing Experiences People Love
moore
138
23k
Build your cross-platform service in a week with App Engine
jlugia
229
18k
Rails Girls Zürich Keynote
gr2m
94
13k
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
33
2k
Unsuck your backbone
ammeep
669
57k
Side Projects
sachag
452
42k
Designing Dashboards & Data Visualisations in Web Apps
destraynor
229
52k
Being A Developer After 40
akosma
87
590k
Evolution of real-time – Irina Nazarova, EuRuKo, 2024
irinanazarova
5
450
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
232
17k
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ライフを!