---
title: "Pagination"
description: "Pagination uses relay style, supporting forward and backward navigation."
lang: en
languages:
  en: https://docs.vismasoftware.no/businessnxtapi/apireference/features/pagination/index.md
lastmod: 2026-07-10
---

> Documentation index: https://docs.vismasoftware.no/businessnxtapi/apireference/features/llms.txt


# Pagination

Last modified July 10, 2026

> Pagination uses relay style, supporting forward and backward navigation.


## Overview

Pagination is implemented in the relay style. Reference documentation can be found at [relay.dev](https://relay.dev/graphql/connections.htm). Both forward and backward pagination are supported:

- the arguments for forward pagination are `first` (specifing the maximum number of items to return) and `after` (which is an opaque cursor, typically pointing to the last item in the previous page)
- the arguments for backward pagination are `last` (specifying the maximum number of items to return) and `before` (which is an opaque cursor, typically pointing to the first item in the next page)
- a `skip` argument is available for both forward and backward pagination and specifies a number of records to be skipped (from the given cursor) before retrieving the requested number of records.

> [!NOTE]
>
> The values for `first`, `last`, and `skip` must be positive integers.

> [!WARNING]
>
> Pagination does not work with grouping (`groupBy` and `having` arguments).

Information about a page is returned in the `pageInfo` node. This contains the following fields:

| Field | Description |
| ----- | ----------- |
| `hasNextPage` | Indicates whether there are more records to fetch after the current page. |
| `hasPreviousPage` | Indicates whether there are more records to fetch before the current page. |
| `startCursor` | An opaque cursor pointing to the first record in the current page. |
| `endCursor` | An opaque cursor pointing to the last record in the current page. |

> [!NOTE]
>
> The cursors are considered opaque, which means that their values should not be interpreted in any way by the client. The client should only use them as arguments for the `after` and `before` parameters when performing pagination. The server uses these cursors to identify the position of records in the table, but the client should not make any assumptions about how this is done. Any such assumption may not hold in the future if the internal implementation and their representation changes.

When forward pagination is performed, the `hasNextPage` field is `true` if there are more records to fetch after the current page. When backward pagination is performed, the `hasNextPage` field is `true` if there are more records to fetch before the current page.

Similarly, when forward pagination is performed, the `hasPreviousPage` field is `true` if there are more records to fetch before the current page. When backward pagination is performed, the `hasPreviousPage` field is always `false`.

## Understanding pagination

In the following example, we fetch the first 5 associtates. This being the first page, there is no argument for the `after` parameter. Passing a `null` value for the `after` parameter is equivalent to not passing it at all.

```graphql { title = "Query" }
query read($cid : Int!)
{
  useCompany(no: $cid)
  {
    associate(first: 5)
    {
      pageInfo
      {
        hasNextPage
        hasPreviousPage
        startCursor
        endCursor
      }
      items
      {
        associateNo
        name
      }
    }
  }
}
```

```graphql { title = "Result" }
{
  "data": {
    "useCompany": {
      "associate": {
        "pageInfo": {
          "hasNextPage": true,
          "hasPreviousPage": false,
          "startCursor": "MQ==",
          "endCursor": "NQ=="
        },
        "items": [
          {
            "associateNo": 1,
            "name": "Et Cetera Solutions"
          },
          {
            "associateNo": 2,
            "name": "Rock And Random"
          },
          {
            "associateNo": 3,
            "name": "Handy Help AS"
          },
          {
            "associateNo": 4,
            "name": "EasyWay Crafting"
          },
          {
            "associateNo": 5,
            "name": "Zen Services AS"
          }
        ]
      }
    }
  }
}
```

For a following page, we need to take the value of the `endCursor` return from a query and supply it as the argument to `after`. This is shown in the following example, where `"NQ=="` from the query above was supplied for the `after` parameter:

```graphql { title = "Query" }
query read($cid : Int!)
{
  useCompany(no: $cid)
  {
    associate(first: 5, after: "NQ==")
    {
      pageInfo
      {
        hasNextPage
        hasPreviousPage
        startCursor
        endCursor
      }
      items
      {
        associateNo
        name
      }
    }
  }
}
```

```graphql { title = "Result" }
{
  "data": {
    "useCompany": {
      "associate": {
        "pageInfo": {
          "hasNextPage": true,
          "hasPreviousPage": true,
          "startCursor": "Ng==",
          "endCursor": "MTA="
        },
        "items": [
          {
            "associateNo": 6,
            "name": "Smart Vita AS"
          },
          {
            "associateNo": 7,
            "name": "Full Force Services"
          },
          {
            "associateNo": 8,
            "name": "HomeRun Auto AS"
          },
          {
            "associateNo": 9,
            "name": "Trade Kraft AS"
          },
          {
            "associateNo": 10,
            "name": "Nodic Cool Sports AS"
          }
        ]
      }
    }
  }
}
```

Requests with backwards pagination are performed similarly, exept that `last` and `before` are used instead of `first` and `after`. In the following example, the value of `before` is taken from the value of `startCursor` returned by the previous query that returned the second page of associates (with five records per page). As a result, the data returned from this new query is actually the first page of associates.

```graphql { title = "Query" }
query read($cid : Int!)
{
  useCompany(no: $cid)
  {
    associate(last: 5, before: "Ng==")
    {
      pageInfo
      {
        hasNextPage
        hasPreviousPage
        startCursor
        endCursor
      }
      items
      {
        associateNo
        name
      }
    }
  }
}
```

```graphql { title = "Result" }
{
  "data": {
    "useCompany": {
      "associate": {
        "pageInfo": {
          "hasNextPage": true,
          "hasPreviousPage": false,
          "startCursor": "MQ==",
          "endCursor": "NQ=="
        },
        "items": [
          {
            "associateNo": 1,
            "name": "Et Cetera Solutions"
          },
          {
            "associateNo": 2,
            "name": "Rock And Random"
          },
          {
            "associateNo": 3,
            "name": "Handy Help AS"
          },
          {
            "associateNo": 4,
            "name": "EasyWay Crafting"
          },
          {
            "associateNo": 5,
            "name": "Zen Services AS"
          }
        ]
      }
    }
  }
}
```

Providing both forward and backward pagination arguments (`first`/`after` and `last`/`before`) is illegal and the query will fail with an error.

If pagination arguments are not supplied a default page size of 5000 records is used.

When you fetch data in pages (which is recommended for most scenarious) you can fetch the total number of objects in the table with disregard to the page that is fetched or the size of the page. This is possible with the `totalCount`. This will return the number of records that match the filter (if any) but ignoring  paginagion (if any).

This feature is exemplified below. We, again, fetch the first 5 associates that have a customer number greater or equal than 11000 but also request the total number of records that match this filter.

```graphql { title = "Query" }
query read($cid : Int!)
{
  useCompany(no: $cid)
  {
    associate(
      first: 5,
      filter: {customerNo :{_gte: 11000}})
    {
      totalCount
      pageInfo
      {
        hasNextPage
        hasPreviousPage
        startCursor
        endCursor
      }
      items
      {
        associateNo
        customerNo
        name
      }
    }
  }
}
```

```graphql { title = "Result" }
{
  "data": {
    "useCompany": {
      "associate": {
        "totalCount": 29,
        "pageInfo": {
          "hasNextPage": true,
          "hasPreviousPage": false,
          "startCursor": "MQ==",
          "endCursor": "NQ=="
        },
        "items": [
          {
            "associateNo": 308,
            "customerNo": 11000,
            "name": "Auto Glory AS"
          },
          {
            "associateNo": 309,
            "customerNo": 11001,
            "name": "Home Team Business"
          },
          {
            "associateNo": 310,
            "customerNo": 11003,
            "name": "Corpus Systems"
          },
          {
            "associateNo": 311,
            "customerNo": 11004,
            "name": "Dash Credit AS"
          },
          {
            "associateNo": 312,
            "customerNo": 11007,
            "name": "Smart Help Services AS"
          }
        ]
      }
    }
  }
}
```

You can skip a number of records before feetching a page (with the size indicated by either the `first` or the `last` arguments) using the argument `skip`. This is an optional argument. When present, it indicates the number of records to be skipped. Here is an example:

```graphql { title = "Forward" }
query read($cid : Int!)
{
  useCompany(no: $cid)
  {
    generalLedgerAccount(skip : 5,
                         first: 10)
    {
      totalCount
      items
      {
        accountNo
      }
    }
  }
}
```

```graphql { title = "Backward" }
query read($cid : Int!)
{
  useCompany(no: $cid)
  {
    generalLedgerAccount(skip : 5,
                         last : 10,
                         before: "MTE=")
    {
      totalCount
      items
      {
        accountNo
      }
    }
  }
}
```

> [!WARNING]
>
> Because the execution of queries for joined tables is optimized for performance, pagination does not work as described in this document. To understand the problem and the workaround see [Unoptimized queries](https://docs.vismasoftware.no/businessnxtapi/apireference/features/unoptimized/index.md).

## Pagination in depth

To understand how pagination works, we will consider the following example, where the general ledger account table contains 25 records. It could look like this:

```
        page 1 (10 records)                    page 2 (10 records)            page 3 (5 records)
|---------------------------------|---------------------------------|---------------------------------|

| 1000 | 1020 | ... | 1100 | 1120 | 1130 | 1140 | ... | 1240 | 1250 | 1260 | 1270 | 1280 | 1300 | 1310 |
```

### Forward pagination

To fetch the records from the beginning, we perform a query like this:

```graphql { title = "Query" }
query read($cid : Int!)
{
  useCompany(no: $cid)
  {
    generalLedgerAccount(first : 10,
                         after: null)
    {
      pageInfo
      {
        hasNextPage
        hasPreviousPage
        startCursor
        endCursor
      }
      items
      {
        accountNo
      }
    }
  }
}
```

```graphql { title = "Result" }
{
  "data": {
    "useCompany": {
      "generalLedgerAccount": {
        "pageInfo": {
          "hasNextPage": true,
          "hasPreviousPage": false,
          "startCursor": "MQ==",
          "endCursor": "MTA="
        },
        "items": [
          {
            "accountNo": 1000
          },
          {
            "accountNo": 1020
          },
          ...
          {
            "accountNo": 1100
          },
          {
            "accountNo": 1120
          }
        ]
      }
    }
  }
}
```

This returns the first ten records, from index 1 to 10, and the `startCursor` and `endCursor` are pointing to the first and last elements in the set.

```
        page 1 (10 records)
|---------------------------------|

| 1000 | 1020 | ... | 1100 | 1120 | 1130 | 1140 | ... | 1240 | 1250 | 1260 | 1270 | 1280 | 1300 | 1310 |
   ^                          ^
   |                          |
 startCursor                endCursor
```

To fetch the next page of 10 records, we provide the value of `endCursor` as the argument for `after`:

```graphql { title = "Query" }
query read($cid : Int!)
{
  useCompany(no: $cid)
  {
    generalLedgerAccount(first : 10,
                         after: "MTA=")
    {
      pageInfo
      {
        hasNextPage
        hasPreviousPage
        startCursor
        endCursor
      }
      items
      {
        accountNo
      }
    }
  }
}
```

```graphql { title = "Result" }
{
  "data": {
    "useCompany": {
      "generalLedgerAccount": {
        "pageInfo": {
          "hasNextPage": true,
          "hasPreviousPage": true,
          "startCursor": "MTE=",
          "endCursor": "MjA="
        },
        "items": [
          {
            "accountNo": 1130
          },
          {
            "accountNo": 1140
          },
          ...
          {
            "accountNo": 1240
          },
          {
            "accountNo": 1250
          }
        ]
      }
    }
  }
}
```

This returns the second page of ten records, from index 11 to 20, and the `startCursor` and `endCursor` are pointing to the first and last elements in the set.

```
                           after            page 2 (10 records)
                             |    |---------------------------------|
                             v
| 1000 | 1020 | ... | 1100 | 1120 | 1130 | 1140 | ... | 1240 | 1250 | 1260 | 1270 | 1280 | 1300 | 1310 |
                                     ^                          ^
                                     |                          |
                                   startCursor                endCursor
```

To fetch the next page of 10 records, we repeat the operation, again, using the `endCursor` for the value of the `after` argument:

```graphql { title = "Query" }
query read($cid : Int!)
{
  useCompany(no: $cid)
  {
    generalLedgerAccount(first : 10,
                         after: "MjA=")
    {
      pageInfo
      {
        hasNextPage
        hasPreviousPage
        startCursor
        endCursor
      }
      items
      {
        accountNo
      }
    }
  }
}
```

```graphql { title = "Result" }
{
  "data": {
    "useCompany": {
      "generalLedgerAccount": {
        "pageInfo": {
          "hasNextPage": false,
          "hasPreviousPage": true,
          "startCursor": "MjE=",
          "endCursor": "MjU="
        },
        "items": [
          {
            "accountNo": 1260
          },
          {
            "accountNo": 1270
          },
          {
            "accountNo": 1280
          },
          {
            "accountNo": 1300
          },
          {
            "accountNo": 1310
          },
        ]
      }
    }
  }
}
```

This returns a page of only 5 records, from index 21 to 25, and the `startCursor` and `endCursor` are pointing to the first and last elements in the set.

```
                                                              after          page 3 (5 records)
                                                                |   |----------------------------------|
                                                                v
| 1000 | 1020 | ... | 1100 | 1120 | 1130 | 1140 | ... | 1240 | 1250 | 1260 | 1270 | 1280 | 1300 | 1310 |
                                                                       ^                           ^
                                                                       |                           |
                                                                     startCursor                 endCursor
 ```

The value of the `hasNextPage` field is `false` because there are no more records to fetch.

> [!NOTE]
>
> The value used for the `after` argument should be the value of the `endCursor` from the previous page (unless it's `null`, in which case it means the fetching should start from the beginning). The `after` cursor identifies the position of the last record in a (previous) page. The record at the position represented by the cursor passed to the `after` argument is not included in the result set. A page starts with the record following the one identified by `after`.

It is also possible to skip records before fetching a page. This is done by providing the `skip` argument. The following example skips the first 3 records before fetching a page of 10 records. We don't start from the beginning, but at the end of the first page of 10 records, as exemplify below:

```
                           after     skip 3 records            page of 10 records
                             |    >-------------------<|-----------------------------------------------|
                             v
| 1000 | 1020 | ... | 1100 | 1120 | 1130 | 1140 | 1150 | 1160 | ... | 1240 | 1250 | 1260 | 1270 | 1280 | 1300 | 1310 |
                                                          ^                                        ^
                                                          |                                        |
                                                        startCursor                              endCursor
```

```graphql { title = "Query" }
query read($cid : Int!)
{
  useCompany(no: $cid)
  {
    generalLedgerAccount(first : 10,
                         skip : 3,
                         after: "MTA=")
    {
      pageInfo
      {
        hasNextPage
        hasPreviousPage
        startCursor
        endCursor
      }
      items
      {
        accountNo
      }
    }
  }
}
```

```graphql { title = "Result" }
{
  "data": {
    "useCompany": {
      "generalLedgerAccount": {
        "pageInfo": {
          "hasNextPage": true,
          "hasPreviousPage": true,
          "startCursor": "MTQ=",
          "endCursor": "MjM="
        },
        "items": [
          {
            "accountNo": 1160
          },
          {
            "accountNo": 1200
          },
          ...
          {
            "accountNo": 1270
          },
          {
            "accountNo": 1280
          }
        ]
      }
    }
  }
}
```

### Backward pagination

Backward pagination works similarly, except that the `last` and `before` arguments are used instead of `first` and `after`. The argument `last` indicates how many records the page should contain. The argument `before` represents the position of the record before which the page is located. The record at the `before` position is not included in the result set.

Therefore, if in the preceding example we fetched all the records in the table, and now want to move backwards, but use the value of the `endCursor` as the arguement for `before`, then this last record will not be included in the returned page.

```
                                                page of 10 records                                 before
                           |---------------------------------------------------------------------| |
                                                                                                   v
| 1000 | 1020 | ... | 1160 | 1200 | 1210 | 1220 | 1230 | 1240 | 1250 | 1260 | 1270 | 1280 | 1300 | 1310 |
                              ^                                                              ^
                              |                                                              |
                            startCursor                                                    endCursor
 ```

```graphql { title = "Query" }
query read($cid : Int!)
{
  useCompany(no: $cid)
  {
    generalLedgerAccount(last : 10,
                         before: "MjU=")
    {
      pageInfo
      {
        hasNextPage
        hasPreviousPage
        startCursor
        endCursor
      }
      items
      {
        accountNo
      }
    }
  }
}
```

```graphql { title = "Result" }
{
  "data": {
    "useCompany": {
      "generalLedgerAccount": {
        "pageInfo": {
          "hasNextPage": true,
          "hasPreviousPage": false,
          "startCursor": "MTU=",
          "endCursor": "MjQ="
        },
        "items": [
          {
            "accountNo": 1200
          },
          {
            "accountNo": 1210
          },
          ...
          {
            "accountNo": 1280
          },
          {
            "accountNo": 1300
          }
        ]
      }
    }
  }
}
```


---

[View this page](https://docs.vismasoftware.no/businessnxtapi/apireference/features/pagination/)
