> ## Documentation Index
> Fetch the complete documentation index at: https://docs.opigno.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Paginating Catalog Items

For paginating the catalog, we use the [<u>GraphQL Cursor Connections Specification</u>](https://relay.dev/graphql/connections.htm), which is thoroughly documented in the official  guidelines.

### **Why Use This Approach?**

Using cursor-based pagination offers several advantages:

* **Efficient Data Handling**: Cursors are more efficient than offset-based pagination, especially with large or frequently updated datasets.
* **Consistency**: It avoids issues with data shifting between requests (e.g., when new trainings are added or removed), ensuring stable navigation through paginated results.
* **Scalability**: Better performance on large datasets, as it avoids skipping records and leverages indexed fields.
* **Built-in Navigation**: The specification provides standard ways to handle forward and backward navigation (e.g., `hasNextPage`, `endCursor`), simplifying client-side implementation.

The `getCatalogData` query includes a `pageInfo` field, which provides metadata about the current result set and allows clients to effectively implement pagination.

```
"pageInfo": {
  "hasNextPage": true,
  "startCursor": "TzozMzoiRHJ1cGFsXG9waWdub19hcGlcV3JhcHBlcnNcQ3Vyc29yIjo0OntzOjE0OiIAKgBiYWNraW5nVHlwZSI7czo5OiJvcGlnbm9fbHAiO3M6MTI6IgAqAGJhY2tpbmdJZCI7aTo0MDtzOjEwOiIAKgBzb3J0S2V5IjtzOjU6IlRJVExFIjtzOjEyOiIAKgBzb3J0VmFsdWUiO3M6NDU6IkhvdyB0byBidWlsZCBIaWdoLUxvYWQgSW5mcmFzdHJ1Y3R1cmUgaW4gMjAyNSI7fQ==",
  "endCursor": "TzozMzoiRHJ1cGFsXG9waWdub19hcGlcV3JhcHBlcnNcQ3Vyc29yIjo0OntzOjE0OiIAKgBiYWNraW5nVHlwZSI7czo5OiJvcGlnbm9fbHAiO3M6MTI6IgAqAGJhY2tpbmdJZCI7aTozOTtzOjEwOiIAKgBzb3J0S2V5IjtzOjU6IlRJVExFIjtzOjEyOiIAKgBzb3J0VmFsdWUiO3M6MTc6IkxvY2tlZCBUcmFpbmluZyAxIjt9"
},
```

#### **Fields:**

* `hasNextPage` (`Boolean`)\
  Indicates whether there are more results available **after** the current page.
  * `true` → Additional pages exist.
  * `false` → You’ve reached the end of the list.
* `startCursor` (`String`)\
  A cursor string representing the **first item** in the current page. It can be used as the `before` parameter to fetch the previous page, if needed.
* `endCursor` (`String`)\
  A cursor string representing the **last item** in the current page. This value should be used as the `after` parameter when querying the **next** page of results.

<Note>
  **Note:** The cursor values (such as the encoded strings shown above) are opaque and should not be parsed or modified by clients—they are meant to be passed back to the API exactly as received.
</Note>

#### **Utilizing pageInfo in the getCatalogData Query**

Building on the explanation of the `pageInfo` fields—`hasNextPage`, `startCursor`, and `endCursor`—these values can now be applied in the `getCatalogData` query to control pagination behavior.

By using the `endCursor` as the `after` argument, clients can request the next page of results. Similarly, the `startCursor` can be passed as the `before` argument to retrieve the previous page. This approach enables efficient, cursor-based pagination that ensures consistent data retrieval, even in dynamic datasets. Please see the full list of available pagination arguments for the `getCatalogData` query:

| Argument | Type   | Description                                                                        |
| -------- | ------ | ---------------------------------------------------------------------------------- |
| `after`  | Cursor | Returns the elements **after** the specified cursor. Use for forward pagination.   |
| `before` | Cursor | Returns the elements **before** the specified cursor. Use for backward pagination. |
| `first`  | Int    | Limits the result to the first `n` elements **after** the `after` cursor.          |
| `last`   | Int    | Limits the result to the last `n` elements **before** the `before` cursor.         |

### **⚖️ Pagination Input Validation**

The `getCatalogData` query supports two mutually exclusive pagination approaches. In both cases, the number of items returned must not exceed the **limit of 100**.

| Pagination Type | Required Argument(s) | Optional Cursor | Constraints                   |
| --------------- | -------------------- | --------------- | ----------------------------- |
| **Forward**     | `first`              | `after`         | `first` must be > 0 and ≤ 100 |
| **Backward**    | `last`               | `before`        | `last` must be > 0 and ≤ 100  |

❌ The following combinations are **not allowed**:

* Providing both `first` and `last`
* Using `first` with `before`
* Using `last` with `after`
* Providing values ≤ 0 or > 100 for `first` or `last`

## **🔁 Loading the Next Page**

To load the next set of catalog items, pass the `endCursor` value from the `pageInfo` object of the previous result into the `after` argument.

<CodeGroup>
  ```GraphQL GraphQL theme={null}
  query getCatalogData {
    getCatalogData(first: 3, after: "TzozMzoiRHJ1cGFsXG9waWdub19hcGlcV3JhcHBlcnNcQ3Vyc29yIjo0OntzOjE0OiIAKgBiYWNraW5nVHlwZSI7czo5OiJvcGlnbm9fbHAiO3M6MTI6IgAqAGJhY2tpbmdJZCI7aTozOTtzOjEwOiIAKgBzb3J0S2V5IjtzOjU6IlRJVExFIjtzOjEyOiIAKgBzb3J0VmFsdWUiO3M6MTc6IkxvY2tlZCBUcmFpbmluZyAxIjt9") {
      edges {
        node {
          cta {
            actionId
            title
            arguments {
              key
              value
            }
            metadata {
              key
              value
            }
          }
          modulesNumber
          activitiesNumber
          training {
            title
            domain {
              name
            }
            image {
              alt
              title
              url
            }
            duration {
              name
            }
            attempt {
              metrics {
                progress
                status
              }
            }
          }
        }
      }
      pageInfo {
        hasNextPage
        startCursor
        endCursor
      }
      totalItems
    }
  }
  ```

  ```Query Query result [expandable] theme={null}
  {
      "data": {
          "getCatalogData": {
              "edges": [
                  {
                      "node": {
                          "cta": [
                              {
                                  "actionId": "VIEW_LP",
                                  "title": "View",
                                  "arguments": [
                                      {
                                          "key": "lpId",
                                          "value": "64"
                                      }
                                  ],
                                  "metadata": []
                              },
                              {
                                  "actionId": "ENROLL_LP",
                                  "title": "Enroll",
                                  "arguments": [
                                      {
                                          "key": "lpId",
                                          "value": "64"
                                      }
                                  ],
                                  "metadata": [
                                      {
                                          "key": "type",
                                          "value": "button"
                                      }
                                  ]
                              }
                          ],
                          "modulesNumber": 3,
                          "activitiesNumber": 4,
                          "training": {
                              "title": "LP Anonymous Free",
                              "domain": [
                                  {
                                      "name": "Domain 1"
                                  },
                                  {
                                      "name": "Domain 2"
                                  },
                                  {
                                      "name": "Domain 2"
                                  },
                                  {
                                      "name": "Domain 1"
                                  }
                              ],
                              "image": [],
                              "duration": [
                                  {
                                      "name": "30"
                                  }
                              ],
                              "attempt": null
                          }
                      }
                  },
                  {
                      "node": {
                          "cta": [
                              {
                                  "actionId": "VIEW_LP",
                                  "title": "View",
                                  "arguments": [
                                      {
                                          "key": "lpId",
                                          "value": "65"
                                      }
                                  ],
                                  "metadata": []
                              },
                              {
                                  "actionId": "LOGIN_AND_ENROLL_LP",
                                  "title": "Login and enroll",
                                  "arguments": [
                                      {
                                          "key": "lpId",
                                          "value": "65"
                                      }
                                  ],
                                  "metadata": []
                              }
                          ],
                          "modulesNumber": 3,
                          "activitiesNumber": 5,
                          "training": {
                              "title": "LP Locked navigation Login and enrol",
                              "domain": [],
                              "image": [],
                              "duration": [
                                  {
                                      "name": "30"
                                  }
                              ],
                              "attempt": null
                          }
                      }
                  },
                  {
                      "node": {
                          "cta": [
                              {
                                  "actionId": "VIEW_LP",
                                  "title": "View",
                                  "arguments": [
                                      {
                                          "key": "lpId",
                                          "value": "62"
                                      }
                                  ],
                                  "metadata": []
                              },
                              {
                                  "actionId": "ENROLL_LP",
                                  "title": "Enroll",
                                  "arguments": [
                                      {
                                          "key": "lpId",
                                          "value": "62"
                                      }
                                  ],
                                  "metadata": [
                                      {
                                          "key": "type",
                                          "value": "button"
                                      }
                                  ]
                              }
                          ],
                          "modulesNumber": 1,
                          "activitiesNumber": 1,
                          "training": {
                              "title": "Training",
                              "domain": [],
                              "image": [],
                              "duration": [],
                              "attempt": null
                          }
                      }
                  }
              ],
              "pageInfo": {
                  "hasNextPage": true,
                  "startCursor": "TzozMzoiRHJ1cGFsXG9waWdub19hcGlcV3JhcHBlcnNcQ3Vyc29yIjo0OntzOjE0OiIAKgBiYWNraW5nVHlwZSI7czo5OiJvcGlnbm9fbHAiO3M6MTI6IgAqAGJhY2tpbmdJZCI7aTo2NDtzOjEwOiIAKgBzb3J0S2V5IjtzOjU6IlRJVExFIjtzOjEyOiIAKgBzb3J0VmFsdWUiO3M6MTc6IkxQIEFub255bW91cyBGcmVlIjt9",
                  "endCursor": "TzozMzoiRHJ1cGFsXG9waWdub19hcGlcV3JhcHBlcnNcQ3Vyc29yIjo0OntzOjE0OiIAKgBiYWNraW5nVHlwZSI7czo5OiJvcGlnbm9fbHAiO3M6MTI6IgAqAGJhY2tpbmdJZCI7aTo2MjtzOjEwOiIAKgBzb3J0S2V5IjtzOjU6IlRJVExFIjtzOjEyOiIAKgBzb3J0VmFsdWUiO3M6ODoiVHJhaW5pbmciO30="
              },
              "totalItems": 10
          }
      }
  }
  ```
</CodeGroup>

<Note>
  In case you reach the end of the catalog, the `pageInfo.hasNextPage` field will be set to `false`
</Note>
