Class: GraphQL::Pagination::Connection

Inherits:
Object
  • Object
show all
Defined in:
lib/graphql/pagination/connection.rb

Overview

A Connection wraps a list of items and provides cursor-based pagination over it.

Connections were introduced by Facebook’s Relay front-end framework, but proved to be generally useful for GraphQL APIs. When in doubt, use connections to serve lists (like Arrays, ActiveRecord::Relations) via GraphQL.

Unlike the previous connection implementation, these default to bidirectional pagination.

Pagination arguments and context may be provided at initialization or assigned later (see Schema::Field::ConnectionExtension).

Direct Known Subclasses

ArrayConnection, RelationConnection

Defined Under Namespace

Classes: Edge, PaginationImplementationMissingError

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(items, parent: nil, field: nil, context: nil, first: nil, after: nil, max_page_size: NOT_CONFIGURED, default_page_size: NOT_CONFIGURED, last: nil, before: nil, edge_class: nil, arguments: nil) ⇒ Connection

Returns a new instance of Connection.

Parameters:

  • items (Object)

    some unpaginated collection item, like an Array or ActiveRecord::Relation

  • context (Query::Context) (defaults to: nil)
  • parent (Object) (defaults to: nil)

    The object this collection belongs to

  • first (Integer, nil) (defaults to: nil)

    The limit parameter from the client, if it provided one

  • after (String, nil) (defaults to: nil)

    A cursor for pagination, if the client provided one

  • last (Integer, nil) (defaults to: nil)

    Limit parameter from the client, if provided

  • before (String, nil) (defaults to: nil)

    A cursor for pagination, if the client provided one.

  • arguments (Hash) (defaults to: nil)

    The arguments to the field that returned the collection wrapped by this connection

  • max_page_size (Integer, nil) (defaults to: NOT_CONFIGURED)

    A configured value to cap the result size. Applied as first if neither first or last are given and no default_page_size is set.

  • default_page_size (Integer, nil) (defaults to: NOT_CONFIGURED)

    A configured value to determine the result size when neither first or last are given.

[View source]

69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/graphql/pagination/connection.rb', line 69

def initialize(items, parent: nil, field: nil, context: nil, first: nil, after: nil, max_page_size: NOT_CONFIGURED, default_page_size: NOT_CONFIGURED, last: nil, before: nil, edge_class: nil, arguments: nil)
  @items = items
  @parent = parent
  @context = context
  @field = field
  @first_value = first
  @after_value = after
  @last_value = last
  @before_value = before
  @arguments = arguments
  @edge_class = edge_class || self.class::Edge
  # This is only true if the object was _initialized_ with an override
  # or if one is assigned later.
  @has_max_page_size_override = max_page_size != NOT_CONFIGURED
  @max_page_size = if max_page_size == NOT_CONFIGURED
    nil
  else
    max_page_size
  end
  @has_default_page_size_override = default_page_size != NOT_CONFIGURED
  @default_page_size = if default_page_size == NOT_CONFIGURED
    nil
  else
    default_page_size
  end
  @was_authorized_by_scope_items = detect_was_authorized_by_scope_items
end

Instance Attribute Details

#after_valueObject

Raw access to client-provided values. (max_page_size not applied to first or last.)


36
37
38
# File 'lib/graphql/pagination/connection.rb', line 36

def after_value
  @after_value
end

#argumentsHash<Symbol => Object>

Returns The field arguments from the field that returned this connection.

Returns:

  • (Hash<Symbol => Object>)

    The field arguments from the field that returned this connection


57
58
59
# File 'lib/graphql/pagination/connection.rb', line 57

def arguments
  @arguments
end

#before_valueObject

Raw access to client-provided values. (max_page_size not applied to first or last.)


36
37
38
# File 'lib/graphql/pagination/connection.rb', line 36

def before_value
  @before_value
end

#contextGraphQL::Query::Context


22
23
24
# File 'lib/graphql/pagination/connection.rb', line 22

def context
  @context
end

#edge_classClass

Returns A wrapper class for edges of this connection.

Returns:

  • (Class)

    A wrapper class for edges of this connection


174
175
176
# File 'lib/graphql/pagination/connection.rb', line 174

def edge_class
  @edge_class
end

#fieldGraphQL::Schema::Field

Returns The field this connection was returned by.

Returns:


177
178
179
# File 'lib/graphql/pagination/connection.rb', line 177

def field
  @field
end

#firstInteger?

Returns A clamped first value. (The underlying instance variable doesn’t have limits on it.) If neither first nor last is given, but default_page_size is present, default_page_size is used for first. If default_page_size is greater than max_page_size, it'll be clamped down to max_page_size. If default_page_size is nil, use max_page_size`.

Returns:

  • (Integer, nil)

    A clamped first value. (The underlying instance variable doesn’t have limits on it.) If neither first nor last is given, but default_page_size is present, default_page_size is used for first. If default_page_size is greater than max_page_size, it'll be clamped down to max_page_size. If default_page_size is nil, use max_page_size`.


143
144
145
146
147
148
149
150
151
# File 'lib/graphql/pagination/connection.rb', line 143

def first
  @first ||= begin
    capped = limit_pagination_argument(@first_value, max_page_size)
    if capped.nil? && last.nil?
      capped = limit_pagination_argument(default_page_size, max_page_size) || max_page_size
    end
    capped
  end
end

#first_valueObject

Raw access to client-provided values. (max_page_size not applied to first or last.)


36
37
38
# File 'lib/graphql/pagination/connection.rb', line 36

def first_value
  @first_value
end

#itemsObject (readonly)

Returns A list object, from the application. This is the unpaginated value passed into the connection.

Returns:

  • (Object)

    A list object, from the application. This is the unpaginated value passed into the connection.


19
20
21
# File 'lib/graphql/pagination/connection.rb', line 19

def items
  @items
end

#lastInteger?

Returns A clamped last value. (The underlying instance variable doesn’t have limits on it).

Returns:

  • (Integer, nil)

    A clamped last value. (The underlying instance variable doesn’t have limits on it)


164
165
166
# File 'lib/graphql/pagination/connection.rb', line 164

def last
  @last ||= limit_pagination_argument(@last_value, max_page_size)
end

#last_valueObject

Raw access to client-provided values. (max_page_size not applied to first or last.)


36
37
38
# File 'lib/graphql/pagination/connection.rb', line 36

def last_value
  @last_value
end

#parentObject

Returns the object this collection belongs to.

Returns:

  • (Object)

    the object this collection belongs to


33
34
35
# File 'lib/graphql/pagination/connection.rb', line 33

def parent
  @parent
end

Instance Method Details

#afterString?

Returns the client-provided cursor. "" is treated as nil.

Returns:

  • (String, nil)

    the client-provided cursor. "" is treated as nil.

[View source]

48
49
50
51
52
53
54
# File 'lib/graphql/pagination/connection.rb', line 48

def after
  if defined?(@after)
    @after
  else
    @after = @after_value == "" ? nil : @after_value
  end
end

#beforeString?

Returns the client-provided cursor. "" is treated as nil.

Returns:

  • (String, nil)

    the client-provided cursor. "" is treated as nil.

[View source]

39
40
41
42
43
44
45
# File 'lib/graphql/pagination/connection.rb', line 39

def before
  if defined?(@before)
    @before
  else
    @before = @before_value == "" ? nil : @before_value
  end
end

#cursor_for(item) ⇒ String

Return a cursor for this item.

Parameters:

  • item (Object)

    one of the passed in #items, taken from #nodes

Returns:

  • (String)

Raises:

[View source]

218
219
220
# File 'lib/graphql/pagination/connection.rb', line 218

def cursor_for(item)
  raise PaginationImplementationMissingError, "Implement #{self.class}#cursor_for(item) to return the cursor for #{item.inspect}"
end

#default_page_sizeObject

[View source]

123
124
125
126
127
128
129
# File 'lib/graphql/pagination/connection.rb', line 123

def default_page_size
  if @has_default_page_size_override
    @default_page_size
  else
    context.schema.default_page_size
  end
end

#default_page_size=(new_value) ⇒ Object

[View source]

118
119
120
121
# File 'lib/graphql/pagination/connection.rb', line 118

def default_page_size=(new_value)
  @has_default_page_size_override = true
  @default_page_size = new_value
end

#edge_nodesObject

Deprecated.

use #nodes instead

A dynamic alias for compatibility with Relay::BaseConnection.

[View source]

186
187
188
# File 'lib/graphql/pagination/connection.rb', line 186

def edge_nodes
  nodes
end

#edgesArray<Edge>

Returns #nodes, but wrapped with Edge instances.

Returns:

  • (Array<Edge>)

    #nodes, but wrapped with Edge instances

[View source]

169
170
171
# File 'lib/graphql/pagination/connection.rb', line 169

def edges
  @edges ||= nodes.map { |n| @edge_class.new(n, self) }
end

#end_cursorString

Returns The cursor of the last item in #nodes.

Returns:

  • (String)

    The cursor of the last item in #nodes

[View source]

211
212
213
# File 'lib/graphql/pagination/connection.rb', line 211

def end_cursor
  nodes.last && cursor_for(nodes.last)
end

#has_default_page_size_override?Boolean

Returns:

  • (Boolean)
[View source]

131
132
133
# File 'lib/graphql/pagination/connection.rb', line 131

def has_default_page_size_override?
  @has_default_page_size_override
end

#has_max_page_size_override?Boolean

Returns:

  • (Boolean)
[View source]

114
115
116
# File 'lib/graphql/pagination/connection.rb', line 114

def has_max_page_size_override?
  @has_max_page_size_override
end

#has_next_pageBoolean

Returns True if there are more items after this page.

Returns:

  • (Boolean)

    True if there are more items after this page

Raises:

[View source]

196
197
198
# File 'lib/graphql/pagination/connection.rb', line 196

def has_next_page
  raise PaginationImplementationMissingError, "Implement #{self.class}#has_next_page to return the next-page check"
end

#has_previous_pageBoolean

Returns True if there were items before these items.

Returns:

  • (Boolean)

    True if there were items before these items

Raises:

[View source]

201
202
203
# File 'lib/graphql/pagination/connection.rb', line 201

def has_previous_page
  raise PaginationImplementationMissingError, "Implement #{self.class}#has_previous_page to return the previous-page check"
end

#max_page_sizeObject

[View source]

106
107
108
109
110
111
112
# File 'lib/graphql/pagination/connection.rb', line 106

def max_page_size
  if @has_max_page_size_override
    @max_page_size
  else
    context.schema.default_max_page_size
  end
end

#max_page_size=(new_value) ⇒ Object

[View source]

101
102
103
104
# File 'lib/graphql/pagination/connection.rb', line 101

def max_page_size=(new_value)
  @has_max_page_size_override = true
  @max_page_size = new_value
end

#nodesArray<Object>

Returns A slice of #items, constrained by @first_value/@after_value/@last_value/@before_value.

Returns:

  • (Array<Object>)

    A slice of #items, constrained by @first_value/@after_value/@last_value/@before_value

Raises:

[View source]

180
181
182
# File 'lib/graphql/pagination/connection.rb', line 180

def nodes
  raise PaginationImplementationMissingError, "Implement #{self.class}#nodes to paginate `@items`"
end

#page_infoObject

The connection object itself implements PageInfo fields

[View source]

191
192
193
# File 'lib/graphql/pagination/connection.rb', line 191

def page_info
  self
end

#range_add_edge(item) ⇒ Edge

This is called by Relay::RangeAdd – it can be overridden when item needs some modifications based on this connection’s state.

Parameters:

  • item (Object)

    An item newly added to items

Returns:

[View source]

158
159
160
# File 'lib/graphql/pagination/connection.rb', line 158

def range_add_edge(item)
  edge_class.new(item, self)
end

#start_cursorString

Returns The cursor of the first item in #nodes.

Returns:

  • (String)

    The cursor of the first item in #nodes

[View source]

206
207
208
# File 'lib/graphql/pagination/connection.rb', line 206

def start_cursor
  nodes.first && cursor_for(nodes.first)
end

#was_authorized_by_scope_items?Boolean

Returns:

  • (Boolean)
[View source]

97
98
99
# File 'lib/graphql/pagination/connection.rb', line 97

def was_authorized_by_scope_items?
  @was_authorized_by_scope_items
end