Skip to content

Commit 18fff57

Browse files
authored
Add support to Relay style pagination (#4)
GraphQL-query-resolver will now follow any relay style pagination queries down to edges and nodes, so we can preload queries that are wrapped in relay queries. Sample relay query: ```graphql query { vendors(first: 5) { edges { node { id name ingredients { name quantity } } cursor } pageInfo { endCursor hasNextPage hasPreviousPage startCursor } } } ```
1 parent 32b44b2 commit 18fff57

File tree

4 files changed

+70
-4
lines changed

4 files changed

+70
-4
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@
88
/spec/reports/
99
/tmp/
1010
.byebug_history
11+
.idea

lib/graphql/query_resolver.rb

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,41 @@ def self.run(model_class, context, return_type)
2222
to_load
2323
end
2424

25-
def self.map_dependencies(class_name, ast_node)
26-
dependencies = {}
25+
def self.using_relay_pagination?(selection)
26+
selection.name == 'edges'
27+
end
28+
29+
def self.map_relay_pagination_depencies(class_name, selection, dependencies)
30+
node_selection = selection.selections.find { |sel| sel.name == 'node' }
31+
32+
if node_selection.present?
33+
map_dependencies(class_name, node_selection, dependencies)
34+
else
35+
dependencies
36+
end
37+
end
38+
39+
def self.has_reflection_with_name?(class_name, selection_name)
40+
class_name.reflections.with_indifferent_access[selection_name].present?
41+
end
42+
43+
def self.map_dependencies(class_name, ast_node, dependencies={})
2744
ast_node.selections.each do |selection|
2845
name = selection.name
2946

30-
if class_name.reflections.with_indifferent_access[selection.name].present?
47+
if using_relay_pagination?(selection)
48+
map_relay_pagination_depencies(class_name, selection, dependencies)
49+
next
50+
end
51+
52+
if has_reflection_with_name?(class_name, name)
3153
begin
3254
current_class_name = selection.name.singularize.classify.constantize
3355
dependencies[name] = map_dependencies(current_class_name, selection)
3456
rescue NameError
3557
selection_name = class_name.reflections.with_indifferent_access[selection.name].options[:class_name]
3658
current_class_name = selection_name.singularize.classify.constantize
3759
dependencies[selection.name.to_sym] = map_dependencies(current_class_name, selection)
38-
3960
next
4061
end
4162
end

spec/graphql/query_resolver_spec.rb

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,4 +98,32 @@
9898

9999
expect(queries.size).to eq(2)
100100
end
101+
102+
it 'works with pagination' do
103+
query = %{
104+
query {
105+
vendors(first: 5) {
106+
edges {
107+
node {
108+
id
109+
name
110+
ingredients {
111+
name
112+
quantity
113+
}
114+
}
115+
cursor
116+
}
117+
pageInfo {
118+
hasNextPage
119+
}
120+
}
121+
}
122+
}
123+
queries = track_queries do
124+
GQL.query(query)
125+
end
126+
127+
expect(queries.size).to eq(2)
128+
end
101129
end

spec/support/graphql_schema.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,14 @@
6060

6161
field :id, types.ID
6262
field :name, types.String
63+
64+
field :ingredients do
65+
type types[IngredientType]
66+
67+
resolve -> (obj, args, ctx) {
68+
obj.ingredients
69+
}
70+
end
6371
end
6472

6573
IngredientType = GraphQL::ObjectType.define do
@@ -97,6 +105,14 @@
97105
end
98106
}
99107
end
108+
109+
connection :vendors, VendorType.connection_type, max_page_size: 50 do
110+
resolve -> (obj, args, ctx) {
111+
GraphQL::QueryResolver::run(Vendor, ctx, VendorType) do
112+
Vendor.all.to_a
113+
end
114+
}
115+
end
100116
end
101117

102118
Schema = GraphQL::Schema.define do

0 commit comments

Comments
 (0)