Skip to main content
@spa-tools/api-client

Pagination

You can run but you cannot hide. Sooner or later you will have to deal with backend data pagination. Even if you don't impelment pagination on the frontend (yay! for infinite scroll), smart backend APIs will not just allow you to silly-nilly request all of a domain's data in one shot, nor should they.

Currently the @spa-tools/api-client has built-in support for two pervasive models of API pagination: skip-based vs token-based. Of course each backend API dictates how pagination is handled, so if you run into an API that uses a different pagination model, please do create an issue on the GitHub repo and we'll see about adding support for it.

Skip-based

Skip-based pagination entails requesting a max number of records to return (aka limit) in conjunction with a number of records to skip where the backend skips ahead to return that number of records along with metadata about the total number of records available as well as the current skip number.

import { callEndpoint } from '@spa-tools/api-client';

const RECORDS_PER_PAGE = 10;

function getProducts(recordSkip = 0) {
return callEndpoint('https://dummyjson.com/products', {
requestOptions: {
// number of records to skip
recordSkip,
// max number of records to return
recordLimit: RECORDS_PER_PAGE,
},
serverModelOptions: {
// query parameter name to use in request URL to represent the desired number of records to skip
recordSkipQueryParamName: 'skip',
// query parameter name to use in request URL to represent the desired record limit
recordLimitQueryParamName: 'limit',
// path to the available total number of records property in the API response body
jsonTotalDotPath: 'total',
},
});
}

const productsResultPage1 = await getProducts();
// --> https://dummyjson.com/products?limit=10&skip=0
console.log(productsResultPage1);

if (productsResultPage1.total > RECORDS_PER_PAGE) {
const productsResultPage2 = await getProducts(RECORDS_PER_PAGE);
// --> https://dummyjson.com/products?limit=10&skip=10
console.log(productsResultPage2);

if (productsResultPage2.total > RECORDS_PER_PAGE * 2) {
const productsResultPage3 = await getProducts(RECORDS_PER_PAGE * 2);
// --> https://dummyjson.com/products?limit=10&skip=20
console.log(productsResultPage3);
}
}

Token-based

Token-based pagination (also referred to as cursor-based) entails requesting a max number of records to return (aka limit) where the backend returns that number of records along with a token (aka cursor) that can be used to request the next set of records.

import { callEndpoint } from '@spa-tools/api-client';

function getProducts(pageToken = '') {
return callEndpoint('https://dummyjson.com/products', {
requestOptions: {
// pagination token for the request
pageToken,
// max number of records to return
recordLimit: 10,
},
serverModelOptions: {
// query parameter name to use in request URL to represent the desired page token
pageTokenQueryParamName: 'pageToken',
// query parameter name to use in request URL to represent the desired record limit
recordLimitQueryParamName: 'limit',
// path to the next page token property in the API response body
jsonNextPageTokenDotPath: 'nextPageToken',
// path to the previous page token property in the API response body
jsonPreviousPageTokenDotPath: 'previousPageToken',
},
});
}

const productsResultPage1 = await getProducts();
// --> https://dummyjson.com/products?limit=10
console.log(productsResultPage1);

if (productsResultPage1.nextPageToken) {
const productsResultPage2 = await getProducts(productsResultPage1.nextPageToken);
// --> https://dummyjson.com/products?limit=10&pageToken={productsResultPage1.nextPageToken}
console.log(productsResultPage2);

if (productsResultPage2.nextPageToken) {
const productsResultPage3 = await getProducts(productsResultPage2.nextPageToken);
// --> https://dummyjson.com/products?limit=10&pageToken={productsResultPage2.nextPageToken}
console.log(productsResultPage3);
}
}