npm package: @lightspeed/graphql-page-connection
Description: Create relay connections from numbered pages pagination
Latest version: 0.1.11

# `@lightspeed/graphql-page-connection`

[![npm version](https://badge.fury.io/js/%40lightspeed%2Fgraphql-page-connection.svg)](https://badge.fury.io/js/%40lightspeed%2Fgraphql-page-connection)

## Introduction

[**Cursor pagination**](https://slack.engineering/evolving-api-pagination-at-slack-1c1f644f8e12) works by returning results after or before a pointer to an item in the dataset. We follow the [Relay Cursor Connections Paginatinon Specification](https://facebook.github.io/relay/graphql/connections.htm) for pagination in GraphQL

Using an opaque string value as a cursor, we can abstract away the differences of underyling pagination schemes per endpoint, while giving us a consistent interface to consumers of the API.

This library provides an abstraction over the **numbered pages pagination model**, in which the data source is expecting a **page number** and a **page size**. The cursor is defined as `base64('page:<page>;offset:<offset>')`.

Let's observe an example in which we request the `first` 4 items `after` cursor `cGFnZTowO29mZnNldDoz`, i.e.: `base64('page:0;offset:3')`.

```ts
{
  "edges": [
    {
      "node": {
        "name": "banana"
      },
      "cursor": "cGFnZToxO29mZnNldDow" // base64('page:1;offset:0')
    },
    {
      "node": {
        "name": "apple"
      },
      "cursor": "cGFnZToxO29mZnNldDox" // base64('page:1;offset:1')
    },
    {
      "node": {
        "name": "pear"
      },
      "cursor": "cGFnZToxO29mZnNldDoy" // base64('page:1;offset:2')
    },
    {
      "node": {
        "name": "watermelon"
      },
      "cursor": "cGFnZToxO29mZnNldDoz" // base64('page:1;offset:3')
    }
  ],
  "pageInfo": {
    "hasNextPage": true,
    "hasPreviousPage": true,
    "startCursor": "cGFnZToxO29mZnNldDow", // base64('page:1;offset:0')
    "endCursor": "cGFnZToxO29mZnNldDoz" // base64('page:1;offset:3')
  },
  "totalCount": 45
}
```

From here, we have a few options:

1. To paginate forward, we request the `first` 4 items `after` end cursor `cGFnZToxO29mZnNldDoz`, i.e.: `base64('page:1;offset:3')`.
   - If we request the `first` 4 items `after` any other cursor, we'll receive an error. The **numbered pages pagination model** data source does not support this request.
1. To paginate backwards, we request the `last` 4 items `before` start cursor `cGFnZToxO29mZnNldDow`, i.e.: `base64('page:1;offset:0')`.
   - If we request the `last` 4 items `before` any other cursor, we'll receive an error. The **numbered pages pagination model** data source does not support this request.

## Quick Start

1. Install the dependency in your webapp.

```sh
yarn add @lightspeed/graphql-page-connection
```

2. Implement your data fetching logic in the resolver.

```ts filename=packages/retail-graphql/modules/product/ProductResolvers.ts
import { pageFromConnectionArgs, connectionFromArray } from '@lightspeed/graphql-page-connection';

export const resolvers: ResolverMap = {
  Query: {
    products: async (_parent, { first, after, last, before }, { dataSources }) => {
      // Extract the page number and page size from cursor arguments
      const connectionArgs = { first, after, last, before };
      const { pageNum, pageSize } = pageFromConnectionArgs(connectionArgs);

      // Retrieve data and page metadata from data source
      const { productDataSource } = dataSources;
      const res = await productDataSource.getProducts({ pageNum, pageSize });
      const { data, pageCount, totalCount } = res;

      // Create a connection as an output
      const meta = { totalCount, pageCount };
      const connection = connectionFromArray(products, connectionArgs, meta);
      return connection;
    },
  },
};
```