class GraphQL::Pagination::Connection
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}).
Attributes
Raw access to client-provided values. (`max_page_size` not applied to first or last.)
@return [Hash<Symbol => Object>] The field arguments from the field that returned this connection
Raw access to client-provided values. (`max_page_size` not applied to first or last.)
@return [GraphQL::Query::Context]
@return [Class] A wrapper class for edges of this connection
@return [GraphQL::Schema::Field] The field this connection was returned by
Raw access to client-provided values. (`max_page_size` not applied to first or last.)
@return [Object] A list object, from the application. This is the unpaginated value passed into the connection.
Raw access to client-provided values. (`max_page_size` not applied to first or last.)
@return [Object] the object this collection belongs to
Public Class Methods
@param items [Object] some unpaginated collection item, like an `Array` or `ActiveRecord::Relation` @param context [Query::Context] @param parent [Object] The object this collection belongs to @param first [Integer, nil] The limit parameter from the client, if it provided one @param after [String, nil] A cursor for pagination, if the client provided one @param last [Integer, nil] Limit parameter from the client, if provided @param before [String, nil] A cursor for pagination, if the client provided one. @param arguments [Hash] The arguments to the field that returned the collection wrapped by this connection @param max_page_size
[Integer, nil] A configured value to cap the result size. Applied as `first` if neither first or last are given.
# File lib/graphql/pagination/connection.rb, line 60 def initialize(items, parent: nil, field: nil, context: nil, first: nil, after: nil, max_page_size: :not_given, 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_given @max_page_size = if max_page_size == :not_given nil else max_page_size end end
Public Instance Methods
@return [String, nil] the client-provided cursor. `“”` is treated as `nil`.
# File lib/graphql/pagination/connection.rb, line 40 def after if defined?(@after) @after else @after = @after_value == "" ? nil : @after_value end end
@return [String, nil] the client-provided cursor. `“”` is treated as `nil`.
# File lib/graphql/pagination/connection.rb, line 31 def before if defined?(@before) @before else @before = @before_value == "" ? nil : @before_value end end
Return a cursor for this item. @param item [Object] one of the passed in {items}, taken from {nodes} @return [String]
# File lib/graphql/pagination/connection.rb, line 178 def cursor_for(item) raise PaginationImplementationMissingError, "Implement #{self.class}#cursor_for(item) to return the cursor for #{item.inspect}" end
A dynamic alias for compatibility with {Relay::BaseConnection}. @deprecated use {#nodes} instead
# File lib/graphql/pagination/connection.rb, line 146 def edge_nodes nodes end
@return [Array<Edge>] {nodes}, but wrapped with Edge
instances
# File lib/graphql/pagination/connection.rb, line 129 def edges @edges ||= nodes.map { |n| @edge_class.new(n, self) } end
@return [String] The cursor of the last item in {nodes}
# File lib/graphql/pagination/connection.rb, line 171 def end_cursor nodes.last && cursor_for(nodes.last) end
@return [Integer, nil]
A clamped `first` value. (The underlying instance variable doesn't have limits on it.) If neither `first` nor `last` is given, but `max_page_size` is present, max_page_size is used for first.
# File lib/graphql/pagination/connection.rb, line 103 def first @first ||= begin capped = limit_pagination_argument(@first_value, max_page_size) if capped.nil? && last.nil? capped = max_page_size end capped end end
# File lib/graphql/pagination/connection.rb, line 94 def has_max_page_size_override? @has_max_page_size_override end
@return [Boolean] True if there are more items after this page
# File lib/graphql/pagination/connection.rb, line 156 def has_next_page raise PaginationImplementationMissingError, "Implement #{self.class}#has_next_page to return the next-page check" end
@return [Boolean] True if there were items before these items
# File lib/graphql/pagination/connection.rb, line 161 def has_previous_page raise PaginationImplementationMissingError, "Implement #{self.class}#has_previous_page to return the previous-page check" end
@return [Integer, nil] A clamped `last` value. (The underlying instance variable doesn't have limits on it)
# File lib/graphql/pagination/connection.rb, line 124 def last @last ||= limit_pagination_argument(@last_value, max_page_size) end
# File lib/graphql/pagination/connection.rb, line 86 def max_page_size if @has_max_page_size_override @max_page_size else context.schema.default_max_page_size end end
# File lib/graphql/pagination/connection.rb, line 81 def max_page_size=(new_value) @has_max_page_size_override = true @max_page_size = new_value end
@return [Array<Object>] A slice of {items}, constrained by {@first_value}/{@after_value}/{@last_value}/{@before_value}
# File lib/graphql/pagination/connection.rb, line 140 def nodes raise PaginationImplementationMissingError, "Implement #{self.class}#nodes to paginate `@items`" end
The connection object itself implements `PageInfo` fields
# File lib/graphql/pagination/connection.rb, line 151 def page_info self end
This is called by `Relay::RangeAdd` – it can be overridden when `item` needs some modifications based on this connection's state.
@param item [Object] An item newly added to `items` @return [Edge]
# File lib/graphql/pagination/connection.rb, line 118 def range_add_edge(item) edge_class.new(item, self) end
@return [String] The cursor of the first item in {nodes}
# File lib/graphql/pagination/connection.rb, line 166 def start_cursor nodes.first && cursor_for(nodes.first) end
Private Instance Methods
# File lib/graphql/pagination/connection.rb, line 198 def decode(cursor) context.schema.cursor_encoder.decode(cursor, nonce: true) end
# File lib/graphql/pagination/connection.rb, line 202 def encode(cursor) context.schema.cursor_encoder.encode(cursor, nonce: true) end
@param argument [nil, Integer] `first` or `last`, as provided by the client @param max_page_size
[nil, Integer] @return [nil, Integer] `nil` if the input was `nil`, otherwise a value between `0` and `max_page_size`
# File lib/graphql/pagination/connection.rb, line 187 def limit_pagination_argument(argument, max_page_size) if argument if argument < 0 argument = 0 elsif max_page_size && argument > max_page_size argument = max_page_size end end argument end