Paging
type-graphql-query supports multiple paging strategies that each have their own pros and cons. This documentation will cover the different paging strategies and their applicable use cases.
The following examples are based on the following ProductDTO:
import { EnhancedField, IDField } from '@lib/type-graphql-query';import { ObjectType, ID, GraphQLISODateTime, Field } from '@lib/type-graphql';
@ObjectType('Product')export class ProductDTO { @IDField(() => ID) id!: string;
@EnhancedField() name!: string;
@EnhancedField() price!: number;
@Field(() => GraphQLISODateTime) created!: Date;
@Field(() => GraphQLISODateTime) updated!: Date;}Cursor Based Paging
Section intitulée « Cursor Based Paging »By default type-graphql-query will expose all query many endpoints as cursor based connections that you can use to page through results.
All cursor connections, regardless of paging strategy, expose the following schema:
scalar ConnectionCursor
type PageInfo { hasNextPage: Boolean hasPreviousPage: Boolean startCursor: ConnectionCursor endCursor: ConnectionCursor}
type ProductConnection { pageInfo: PageInfo! edges: [ProductEdge!]!}
type ProductEdge { node: Product! cursor: ConnectionCursor!}Offset Based Cursor
Section intitulée « Offset Based Cursor »By default all cursors will use a form of offset based paging to back cursors.
| Pros | Cons |
|---|---|
| Allow for recursive relation paging | Pages and nodes may change between queries |
| Straight forward to implement | Inconsistent Sorting - natural sorting is used by default |
| Queries grow more inefficient as you go through more pages |
Example:
{ products(paging: { first: 2 }) { pageInfo { hasNextPage hasPreviousPage startCursor endCursor } edges { node { id name price created updated } cursor } }}Response:
{ "data": { "products": { "pageInfo": { "hasNextPage": true, "hasPreviousPage": false, "startCursor": "YXJyYXljb25uZWN0aW9uOjA=", "endCursor": "YXJyYXljb25uZWN0aW9uOjE=" }, "edges": [ { "node": { "id": "1", "name": "Premium Eggs", "price": 15.99, "created": "2021-02-14T12:56:01.106Z", "updated": "2021-02-14T12:56:01.106Z" }, "cursor": "YXJyYXljb25uZWN0aW9uOjA=" }, { "node": { "id": "2", "name": "Organic Eggs", "price": 18.99, "created": "2021-02-14T12:56:01.106Z", "updated": "2021-02-14T12:56:01.106Z" }, "cursor": "YXJyYXljb25uZWN0aW9uOjE=" } ] } }}Key Set Based Cursor
Section intitulée « Key Set Based Cursor »You have the option to specify a key set on your DTO which will replace the offset with a where clause. A keyset is a set of fields that uniquely identify a record (e.g. id).
| Pros | Cons |
|---|---|
| Consistent Results - All cursors uniquely identify a record allowing for deterministic before/after pages | Unable to do recursive paging for relations |
| Stable Sorting - All results will be deterministically sorted | A unique key set must be specified |
| Consistent performance with correct indexes in place | Unable to jump to arbitrary pages |
To use key set based cursors, annotate each sort field with @KeySetField. Keyset mode is enabled automatically when at least one @KeySetField is present on the DTO.
import { EnhancedField, IDField, QueryOptions, KeySetField, PagingStrategies,} from '@lib/type-graphql-query';import { ObjectType, ID } from '@lib/type-graphql';
@ObjectType('Product')@QueryOptions({ pagingStrategy: PagingStrategies.CURSOR })export class ProductDTO { @IDField(() => ID) @KeySetField({ order: 1 }) id!: string;
@EnhancedField() name!: string;
// ... other fields}Offset Based Paging
Section intitulée « Offset Based Paging »When using offset based paging, the paging argument accepts limit and offset values, and the result will be an OffsetConnection with nodes instead of edges.
Example:
{ products(paging: { limit: 2 }) { pageInfo { hasNextPage hasPreviousPage } nodes { id name price created updated } }}Response:
{ "data": { "products": { "pageInfo": { "hasNextPage": true, "hasPreviousPage": false }, "nodes": [ { "id": "1", "name": "Premium Eggs", "price": 15.99, "created": "2021-02-14T12:56:01.106Z", "updated": "2021-02-14T12:56:01.106Z" }, { "id": "2", "name": "Organic Eggs", "price": 18.99, "created": "2021-02-14T12:56:01.106Z", "updated": "2021-02-14T12:56:01.106Z" } ] } }}In this example we’ll also pass in an offset to fetch the next 2 records:
GraphQL:
{ products(paging: { limit: 2, offset: 2 }) { pageInfo { hasNextPage hasPreviousPage } nodes { id name price created updated } }}Response:
{ "data": { "products": { "pageInfo": { "hasNextPage": true, "hasPreviousPage": true }, "nodes": [ { "id": "3", "name": "Free Range Eggs", "price": 20.99, "created": "2021-02-14T12:56:01.106Z", "updated": "2021-02-14T12:56:01.106Z" }, { "id": "4", "name": "Pasture Raised Eggs", "price": 22.99, "created": "2021-02-14T12:56:01.106Z", "updated": "2021-02-14T12:56:01.106Z" } ] } }}Disabling Paging
Section intitulée « Disabling Paging »When using the NONE paging strategy, the paging argument is removed and all methods will return an ArrayConnection.
| Pros | Cons |
|---|---|
| All data is returned at once | Not suitable for large datasets |
To disable paging, you can set the pagingStrategy option using the @QueryOptions decorator to PagingStrategies.NONE:
import { EnhancedField, IDField, QueryOptions, PagingStrategies } from '@lib/type-graphql-query';import { ObjectType, ID, GraphQLISODateTime, Field } from '@lib/type-graphql';
@ObjectType('Product')@QueryOptions({ pagingStrategy: PagingStrategies.NONE })export class ProductDTO { @IDField(() => ID) id!: string;
@EnhancedField() name!: string;
@EnhancedField() price!: number;
@Field(() => GraphQLISODateTime) created!: Date;
@Field(() => GraphQLISODateTime) updated!: Date;}When using this strategy, queries that return multiple records will return an array instead of a connection:
GraphQL:
{ products { id name price created updated }}Response:
{ "data": { "products": [ { "id": "1", "name": "Premium Eggs", "price": 15.99, "created": "2020-06-12T08:15:18.876Z", "updated": "2020-06-12T08:15:18.876Z" }, { "id": "2", "name": "Organic Eggs", "price": 18.99, "created": "2020-06-12T08:15:18.876Z", "updated": "2020-06-12T08:15:18.876Z" }, { "id": "3", "name": "Free Range Eggs", "price": 20.99, "created": "2020-06-12T08:15:18.876Z", "updated": "2020-06-12T08:15:18.876Z" } ] }}