input StepZen_RESTPagination

Defines how @rest supports pagination.

StepZen maps standard REST pagination styles into the standard GraphQL Cursor Connections Specification. This allows clients of the endpoint to then page through values consistently regardless of how the back-end supports pagination.

@rest supports forward cursors with NEXT_CURSOR, OFFSET and PAGE_NUMBER pagination types using the standard pagination arguments first and after. Note that use of the paginated query is identical for the client regardless of if @rest is configured to use NEXT_CURSOR, OFFSET or PAGE_NUMBER.

A paged REST call must return (after any transformation):

  • A array of the values to be paged. For example when paging Customer type, the JSON returns an array of objects than can be extracted into the Customer type. resultroot is typically used to location the list of values.

  • Paging meta data, e.g. the total number of values, depending on the pagination type.

StepZen will extract the values and meta data into the connection structure (edges, nodes & PageInfo) required by the GraphQL Cursor Connections Specification. Thus the REST backend and the @rest configuration is not concerned with the connection and edge types.

See the examples under each cursor type.

NEXT_CURSOR support

  • the client passes after as the empty string or null for the first request, depending on how the backend api service supports next cursors. Typically this is handled by the default value for after being the initial cursor value.

  • if after is the empty string then the assumption is it's the first request

  • for subsequent requests the client passes after as the the opaque cursor value of connection.pageInfo.endCursor of the previous request.

  • the opaque cursor after argument is unpacked to contain backend api service's next cursor value when used in the context of @rest, for example as $after in endpoint.

  • the virtual field nextCursor must be set from the response using StepZen_PaginationType.setters.

  • if the backend does not accept an initial value for its next cursor value then the after argument must be defined as after:String (nullable).

An example:

  customers(first:Int! =20 after:String! =""): CustomerConnection
    @rest(
      endpoint:"https://api.example.com/customers?limit=$first&next=$after"
      resultroot:"data[]"
      pagination: {
          type: NEXT_CURSOR
          setters: [{field:"nextCursor" path: "meta.next"}]
        }
      )
  • The first request passes no GraphQL field arguments to customers and then within the context of the @rest directive expansion variables first=20 and after="" (next cursor) are set. Leading to a URL of https://api.example.com/customers?limit=20&next=.

  • The next request then passes the opaque cursor value from PageInfo.endCursor as Query.customers(after:) (for example: after:"eyJjI...vIjo1fQ") and then within the context of the @rest directive first=20 and after="j8sad...jwd" (backend specific next cursor). Leading to a URL of https://api.example.com/customers?limit=20&next=j8sad...jwd.

  • Opaque cursors at the GraphQL layer are not the same raw values as the backend's opaque next cursor values.

OFFSET support

  • the client passes after as the empty string or null for the first request. Typically this is handled by the default value for after being the empty string.

  • if after is the empty string then the assumption is it's the first request

  • for subsequent requests the client passes after as the the opaque cursor value of connection.pageInfo.endCursor of the previous request.

  • the opaque cursor after argument is unpacked to contain backend api service's offset integer value when used in the context of @rest, for example as $after in endpoint. The offset is zero based, so the first edge in the paged set has offset zero.

  • the virtual field total can be set from the response using StepZen_PaginationType.setters to be the total number of edges (records) in the paged set.

  • if the backend api service does not make the total number of edges available then no setter for total is specified. In this case the backend api service must return full pages (matching first) until the paged set is exhausted.

An example:

  customers(first:Int! =20 after:String! =""): CustomerConnection
    @rest(
      endpoint:"https://api.example.com/customers?limit=$first&offset=$after"
      resultroot:"data[]"
      pagination: {
          type: OFFSET
          setters: [{field:"total" path: "meta.total_count"}]
        }
      )
  • The first request passes no GraphQL field arguments to customers then within the context of the @rest directive expansion variables first=20 and after=0 (edge offset) are set. Leading to a URL of https://api.example.com/customers?limit=20&offset=0.

  • The next request then passes the opaque cursor value from PageInfo.endCursor as Query.customers(after:) (for example: after:"eyJjI...vIjo1fQ") and then within the context of the @rest directive first=20 and after=20. Leading to a URL of https://api.example.com/customers?limit=20&offset=20.

PAGE_NUMBER support

  • the client passes after as the empty string or null for the first request. Typically this is handled by the default value for after being the empty string.

  • if after is the empty string then the assumption is it's the first request

  • for subsequent requests the client passes after as the the opaque cursor value of connection.pageInfo.endCursor of the previous request.

  • the opaque cursor after argument is unpacked to contain backend api service's page number integer value when used in the context of @rest, for example as $after in endpoint. The page number is one based, so the first edge in the paged set will be from page 1.

  • the number of edges per page is set from the initial first argument value. subsequent requests must set the same value for first.

  • the virtual field total must be set from the response using StepZen_PaginationType.setters to be the total number of pages in the paged set.

An example:

  customers(first:Int! =20 after:String! =""): CustomerConnection
    @rest(
      endpoint:"https://api.example.com/customers?page=$after&per_page=$first"
      resultroot:"data[]"
      pagination: {
          type: PAGE_NUMBER
          setters: [{field:"total" path: "meta.total_pages"}]
        }
      )
  • The first request passes no GraphQL field arguments to customers then within the context of the @rest directive expansion variables first=20 and after=1 (page number) are set. Leading to a URL of https://api.example.com/customers?page=1&per_page=20.

  • The next request then passes the opaque cursor value from PageInfo.endCursor as Query.customers(after:) (for example: after:"eyJjI...vIjo1fQ") and then within the context of the @rest directive first=20 and after=2. Leading to a URL of https://api.example.com/customers?page=2&per_page=20.

Fields

setters: [StepZen_FieldSetter!]

How virtual fields are set from the response. Since typically pagination is meta-data in the response path is always relative to the root of the actual response, resultroot is not used. Pagination type indicates which virtual fields must be set.

type: StepZen_PaginationType!

How pagination is handled by the back-end suppporting @rest.