DEV Community

Mcdavid Emereuwa
Mcdavid Emereuwa

Posted on • Edited on

How to paginate your NodeJS APIs

Why paginate?

A lot of times when we make calls to a large REST API a lot of results is been returned, hence the need to paginate or break down the results into chunks to make the responses easier to handle.

What to expect

I'll be sharing code-snippets on how I handle pagination when building APIs with Node when querying a Javascript Array when making calls to a MongoDB database with mongoose or using sequelize for SQL databases (PostgreSQL/MySQL)

First you'll need the NPM package paginate-info you can install it by running:

$ npm install -S paginate-info
Enter fullscreen mode Exit fullscreen mode

Javascript Array

You can copy this example JSON file and create your own data.json file.

Now let's see how we can paginate the Javascript Array

import { calculateLimitAndOffset, paginate } from 'paginate-info';
import data from './data.js'

const getAllData = (req, res) => {
  const { query: { currentPage, pageSize } } = req;
  const { limit, offset } = calculateLimitAndOffset(currentPage, pageSize);
  const count = data.length;
  const paginatedData = data.slice(offset, offset + limit);
  const paginationInfo = paginate(currentPage, count, paginatedData);

  return res.status(200).json({
    success: true,
    data: { result: paginatedData, meta: paginationInfo }
  });
}
Enter fullscreen mode Exit fullscreen mode

From the above code we are getting our page values currentPage and pageSize from the request query and passing it to the paginate-info calculateLimitAndOffset function, which in turn returns to us our limit and offset. You can read more of how this works by visiting the docs for paginate-info

If for example we have 100 documents in our array and we decided to get the second page with a page size of 20 elements.
req.query.currentPage = 2, req.query.pageSize= 10.

Mongoose implementation(MongoDB)

/--- Start of MongoDB example --- /
import { calculateLimitAndOffset, paginate } from 'paginate-info';

/**
   * @function getAll
   * @param {Object} req request object
   * @param {Object} res response object
   * @returns {Object} response object
   * @description gets all available results
   */
  const getAll = async (req, res) => {
    const {
      query: { currentPage, pageSize }
    } = req;
    try {
      const count = await model.estimatedDocumentCount();
      const { limit, offset } = calculateLimitAndOffset(page, pageSize);
      const rows = await model.find({})
        .limit(limit)
        .skip(offset);
      const meta = paginate(currentPage, count, rows, pageSize);
      return handleServerResponse(res, 200, { rows, meta });
    } catch (error) {
      return handleServerError(res, error);
    }
  }
  /----End of MongoDB implementation---/
Enter fullscreen mode Exit fullscreen mode

Like with our array example we are getting our currentPage and pageSize from our request query. We get our get the total count of our documents using the estimatedDocumentCount provided by Mongoose. We pass the limit and offset generated from our calculateLimitAndOffset function to the mongoose find() limit and skip function respectively. Our meta data is generated the same way as the array.

Sequelize (PostgreSQL/MYSQL) implementation


 * @function getAll
 * @param {Object} req - server request
 * @param {Object} res - server response
 * @returns {Object} - custom response
*/
const getAll = async (req, res) => {
  try {
    const {
      query: {
        currentPage, pageSize
      }
    } = req;
    const { limit, offset } = calculateLimitAndOffset(currentPage, pageSize);
    const { rows, count } = await model.findAndCountAll({ limit, offet});
    const meta = paginate(currentPage, count, rows, pageSize);
    return response(res, 200, 'success', { rows, meta });
  } catch (error) {
    return response(res, 500, 'error', serverError);
  }
};

/** End of PostgreSQL/SQL(sequelize) implementation */
Enter fullscreen mode Exit fullscreen mode

So for the SQL implementation its the same routine, only that this time we use sequelize's findAndCountAll method to get both the documents and the count, destructured as rows and count respectively.

So that's all for pagination from me. For more info as to the paginate-info package, check it out here on NPM.

I'll appreciate questions and your feedback in the response section.

Top comments (6)

Collapse
 
milburngomes profile image
MilburnGomes

Great Article!! Very informative and useful. Can you tell me how to invoke this call (URL) in nodejs?

Collapse
 
mcdavid95 profile image
Mcdavid Emereuwa • Edited

Something like GET base_url/api/v1/users?currentPage=2&pageSize=10

Thanks for the feedback, does this work?

Collapse
 
milburngomes profile image
MilburnGomes

Yes! Thank you so much.

Thread Thread
 
mcdavid95 profile image
Mcdavid Emereuwa

You are welcome, glad to be of help.

Collapse
 
dani3lrp profile image
Daniel Roa Petrasic

Hello! PageCount always gives me the same value which is 7. It is not counting the number of pages (?). Regards

Collapse
 
sagar2agrawal profile image
Sagar Agrawal

Not sure, but i think, skip should come first and then the limit.

Else it will get wrong paged data (i guess).