Pagination in React, Node.js and MongoDB

In this tutorial, we will show you, how you can do pagination in your React app having Node.js and MongoDB as backend. Even if you have any other backend technology, you can still get the algorithm and apply it in your language.

React

Following is our React component that will call an AJAX request and fetch the data from API. After fetching the data from API, it will render the data along with the pagination links. Whenever any of the pagination link is clicked, it will refetch the data based on the clicked page number.

<script src="js/babel.min.js"></script>
<script src="js/react.development.js"></script>
<script src="js/react-dom.development.js"></script> 

<div id="app"></div>

<script type="text/babel">

    function App() {

        // Set current page
        const [page, setPage] = React.useState(1);

        // Number of pages on pagination
        const [pages, setPages] = React.useState(0);

        // Whenever it's value is updated, the data will be re-fetched
        const [refetch, setRefetch] = React.useState(0);

        // Data array
        const [users, setUsers] = React.useState([]);

        // Check if there are more pages
        const [hasMorePages, setHasMorePages] = React.useState(false);

        async function loadMore() {
            // Creating a built-in AJAX object
            var ajax = new XMLHttpRequest();

            // Tell the method, URL of request and make is asynchronous
            ajax.open("POST", "http://localhost:3000/fetch-users", true);

            // Detecting request state change
            ajax.onreadystatechange = function () {

                // Called when the response is successfully received
                if (this.readyState == 4) {

                    if (this.status == 200) {
                        // For debugging purpose only
                        // console.log(this.responseText);

                        // Converting JSON string to Javasript array
                        var response = JSON.parse(this.responseText);

                        // Update state variable
                        setUsers(response.users);

                        // Set if there are more pages
                        setHasMorePages(response.hasMorePages);

                        // Set pagination
                        setPages(response.pages);
                    }
                }
            };

            // Create form data object
            const formData = new FormData();

            // Attach current page number
            formData.append("page", page);

            // Send the request
            ajax.send(formData);
        }

        // Run function when page value updates
        React.useEffect(function () {
            loadMore();
        }, [refetch]);

        return (
            <>
                {/* Display data */}

                { users.map(function (user) {
                    return (
                        <p key={ `user-${ user._id }` }>{ user.name }</p>
                    );
                }) }

                {/* Show pagination if required */}
                
                { pages > 1 && (
                    <nav>
                        <ul>
                            { Array.from({ length: pages }, function (_, index) { return index + 1 } ).map(function (p) {
                                return (
                                    <li key={ `pagination-${ p }` } className={ `page-item ${ p == page ? "active" : "" }` }>
                                        <a href="#" onClick={ function (event) {
                                            event.preventDefault();
                                            setPage(p);
                                            setRefetch(refetch + 1);
                                        } }>{ p }</a>
                                    </li>
                                );
                            }) }
                        </ul>
                    </nav>
                ) }
            </>
        );

    }

    ReactDOM.createRoot(
        document.getElementById("app")
    ).render(<App />);

</script>

Node.js and MongoDB

Now in our Node.js server, we will create an API that will return all the records based on the current page from client side. It will also return the number of pagination links.

const express = require("express");
const app = express();
const http = require("http").createServer(app);

const mongodb = require("mongodb");
const ObjectId = mongodb.ObjectId;
const MongoClient = mongodb.MongoClient;

const cors = require("cors");
app.use(cors("http://localhost/pagination-in-react-node-js-and-mongodb"));

const expressFormidable = require("express-formidable");
app.use(expressFormidable({
    multiples: true
}));

const port = process.env.PORT || 3000;
const databaseName = "test"
const MONGO_URI = process.env.MONGO_URI || "mongodb://127.0.0.1:27017";

http.listen(port, async function () {
    console.log("Server started");

    const client = new MongoClient(MONGO_URI);
    try {
        await client.connect();
        const db = client.db(databaseName);
        console.log("Database connected.");

        app.post("/fetch-users", async function (request, result) {
            const limit = 2;
            const page = parseInt(request.fields.page || 1);
            const skip = (page - 1) * limit;

            const users = await db.collection("users")
                .find({})
                .sort({
                    _id: 1
                })
                .skip(skip)
                .limit(limit)
                .toArray();

            const usersArr = [];
            for (let a = 0; a < users.length; a++) {
                const obj = {
                    _id: users[a]._id || "",
                    name: users[a].name || ""
                };

                usersArr.push(obj);
            }

            const totalCount = await db.collection("users").countDocuments({});
            const hasMorePages = (page * limit) < totalCount;
			const pages = Math.ceil(totalCount / limit);

            result.json({
                status: "success",
                message: "Data has been fetched.",
                users: users,
                hasMorePages: hasMorePages,
                pages: pages
            });
        });

    } catch (exp) {
        console.log(exp.message);
    }
});

That’s how you can create pagination links in your React app with Node.js and MongoDB as backend. If you face any problem in following this, feel free to contact me.

Pagination on arrays – MongoDB

In this tutorial, we will teach you, how you can do pagination on arrays of documents in MongoDB.

If you have a very large database, fetching all the documents in one query might be very slow. But we have a solution: Pagination. It allows you to fetch a few records in one query and the next few in another query. For example, if you have 1000 users, the first query will fetch users from 0 to 100, the second query will fetch users from 101 to 200, and so on.

Problem

Pagination comes with a problem; when data is stored in arrays. In Mongo DB, the data is stored in JSON format. You might be saving data in arrays. For example, take the following document:

db.collection("users").insertOne({
	name: "Adnan",
	workouts: [{
		name: "Push ups",
		sets: 10,
		reps: 30
	}, {
		name: "Pull ups",
		sets: 7,
		reps: 10
	}, {
		name: "Crunches",
		sets: 5,
		reps: 50
	}, {
		name: "Deadlifts",
		sets: 3,
		reps: 15
	}, {
		name: "Shoulder press",
		sets: 8,
		reps: 8
	}]
})

Here, one document has an array of “workouts” and it might have a lot more data in it. For example, the user we created above has 5 “workouts” array elements, but you want to show 2 workouts on each query. If you run the regular query to fetch the users document, then it will return all the 5 workouts:

// not a solution

// return the whole object
const data = await db.collection("users").find({
	name: "Adnan"
}).toArray()
console.log(data[0])

But it will return the whole object with all 5 “workouts” elements, which is not the desired outcome.

So how will you paginate on that ?

Solution

This is where the Mongo DB $slice operator comes in. It works the same as the Javascript slice() function. It cuts the array from provided start and end values. So we will use the following query to get the users document but with a few workouts:

// solution

// return the first 2 workouts
const data = await db.collection("users").find({
	name: "Adnan"
}, {
	projection: {
		workouts: {
			$slice: [0, 2]
		}
	}
}).toArray()
console.log(data[0])

Slice start value works on indexes, so 0 means the first element of the array, and the end means the number of elements to fetch. So it will fetch 2 array elements starting at index 0.

The second parameter to the find() function tells the projection, which means which keys you want to fetch. You have to write all your key names inside the “projection” object. Now if you want to fetch the next 2 records, you can simply do the following:

// return the workouts from 2 to 4 
const data = await db.collection("users").find({
	name: "Adnan"
}, {
	projection: {
		workouts: {
			$slice: [2, 2]
		}
	}
}).toArray()
console.log(data[0])

The “start” is set to 2, which means the array starts from index 2 which will be the 3rd element of the array. Because the first and second are already fetched in the previous query. The “end” is again set to 2 so we are again fetching the 2 records from the array.

That’s how you can keep going on and implementing pagination on your document’s arrays in MongoDB. If you face any problem in following this, kindly do let me know.

[wpdm_package id=’2045′]

Pagination – Node JS, Mongo DB, Express

In this tutorial, we are going to create a simple pagination program in Node JS and Mongo DB. For the sake of simplicity, we will be using the Express framework. If you prefer a video tutorial over a textual tutorial, then you can follow the video tutorial below:

Video tutorial

Pagination – Node JS, Mongo DB, Express

Mongo DB data

First, we are going to fill the data in Mongo DB. You can apply this tutorial to your own collections and documents. Or you can import the following (at the end) JSON file in your database using Mongo DB compass. You need to create a database named “pagination_nodejs_mongodb” before importing the file.

Setup the project

Create an empty new folder and enter it with the following commands from Terminal:

mkdir pagination-nodejs-mongodb
cd pagination-nodejs-mongodb

Once in the folder, you can install the required modules by running the following commands:

npm install express http mongodb ejs
npm install -g nodemon

The express framework is used to create web applications and APIs, and we will be creating an API to fetch the data for pagination. HTTP module will be used to start the server at a specific port. Mongo DB module will be used to connect and interact with the Mongo DB database. And finally, EJS will be used to render HTML files.

Create a file named “server.js” and write the following code in it:

// initialize express framework
var express = require("express");
var app = express();

// create http server
var http = require("http").createServer(app);

// start the server
http.listen(3000, function () {
    console.log("Server started at port 3000");
});

Run the following command to start the server.

nodemon server.js

This will start the server at port 3000. If you open your terminal, you will see the above message.

Pagination

Now you need to include the Mongo DB module, write the following lines before line 8.

// include mongo DB module
var mongodb = require("mongodb");
var mongoClient = mongodb.MongoClient;
var ObjectId = mongodb.ObjectId;

Then you can connect with the Mongo DB server. You need to write the following lines inside http.listen function callback (2nd parameter):

// connect with mongo DB server and database
mongoClient.connect("mongodb://localhost:27017", {
    useUnifiedTopology: true
}, function (error, client) {
    var database = client.db("pagination_nodejs_mongodb");
    console.log("Database connected.");
});

If you check your terminal now, you will see a second message that your database has been connected. Now we need to create a GET route which when accessed will display 2 users per page from the users collection data. So paste the following lines after the database is connected:

// create a GET HTTP route
app.get("/", async function (request, result) {

    // number of records you want to show per page
    const perPage = 2

    // total number of records from database
    const total = await database.collection("users").countDocuments()

    // Calculating number of pagination links required
    const pages = Math.ceil(total / perPage)

    // get current page number
    const pageNumber = parseInt(request.query.page || 1)

    // get records to skip
    const startFrom = (pageNumber - 1) * perPage

    // get data from mongo DB using pagination
    var users = await database.collection("users").find({})
        .sort({ "id": -1 })
        .skip(startFrom)
        .limit(perPage)
        .toArray();

    // render an HTML page with number of pages, and users data
    result.render("index", {
        "pages": pages,
        "users": users
    });
});

Comments have been added with each line for an explanation. To display the HTML file, we need to tell the Express framework that we will be using the EJS engine. Write the following line before the http.listen function:

// set the view engine as EJS for displaying HTML files
app.set("view engine", "ejs");

Create a folder named “views” at the root of your project. Inside the views folder, create a file named “index.ejs”. Following will be the content of “index.ejs” file:

<table>
    <tr>
        <th>Name</th>
        <th>Age</th>
    </tr>

    <tbody>
        <% for (var a = 0; a < users.length; a++) { %>
        <tr>
            <td><%= users[a].name  %></td>
            <td><%= users[a].age  %></td>
        </tr>
        <% } %>
    </tbody>
</table>

<ul class="pagination">
    <% for (var a = 1; a <= pages; a++) { %>
    <li>
        <a href="?page=<%= a; %>">
            <%= a; %>
        </a>
    </li>
    <% } %>
</ul>

You can run the project from browser by accessing the URL: http://localhost:3000/ and you will see 2 pages on first page along with pagination links at the bottom. On clicking the other page link, you will see further records.

[wpdm_package id=’1232′]

Dynamic Pagination – PHP

Dynamic pagination is one of the most amazing way to optimize your page load if you have a lot of record in your database. You can see it in a tech giant Google, when you search for something, you will see a list of page numbers at the bottom of page. We will be using a sample database called classicmodels and we are assuming to display 5 records in one page.

Creating a layout

Our layout should be super simple, a simple bootstrap table and an unordered list for page number list:

<table class="table">
    <tr>
        <th>Employee number</th>
        <th>First name</th>
        <th>Last name</th>
        <th>Email</th>
    </tr>
</table>

Displaying dynamic pagination links

To display pagination links, we have to divide total rows by the number of records we want to display in 1 page. For example, if you have 30 records in your database and you want to display 5 records in 1 page, the formula should be:

30 / 5 = 6

Thus, 6 links will be displayed in an unordered list:

// Connecting with database
$connection = mysqli_connect("localhost", "root", "", "classicmodels");
// How many records will be displayed in one page
$record_per_page = 5;
// Getting total number of records
$sql = "SELECT COUNT(*) AS total FROM employees";
$result = mysqli_query($connection, $sql);
$total = mysqli_fetch_object($result)->total;
// Calculating number of pagination links required
$pages = ceil($total / $record_per_page);

Then we can simply loop till $pages variable and display the list items:

<ul class="pagination">
    <?php for ($a = 1; $a <= $pages; $a++): ?>
        <li class="<?php echo $a == $page_number ? 'active' : ''; ?>">
            <a href="?page=<?php echo $a; ?>">
                <?php echo $a; ?>
            </a>
        </li>
    <?php endfor; ?>
</ul>

At this point, if you run the file you will only see pagination links. On clicking you will see $page variable in the URL. We will be using this to display records as per that page.

Displaying records based on page number

Before the <table> tag, you need to check if there is $page variable in the URL. If not, then the variable should have value 1, by default. To append that variable in MySQL query, we need to subtract 1 from it as the index starts from 0. Then we will multiply it with 5 (number of records in 1 page). So your basic formula should be:

($page_number – 1) * 5

So if you are on page 3, then putting the values you will get:

(3 – 1) * 5 = 10

Thus, it will get records from 10 to 15. Below is the code for it:

$page_number = isset($_GET["page"]) ? $_GET["page"] : 1;
$start_from = ($page_number - 1) * $record_per_page;
$sql = "SELECT * FROM employees LIMIT " . $start_from . ", " . $record_per_page;
$result = mysqli_query($connection, $sql);

And to display it in <table> you can do:

<?php while ($row = mysqli_fetch_object($result)): ?>
    <tr>
        <td><?php echo $row->employeeNumber; ?></td>
        <td><?php echo $row->firstName; ?></td>
        <td><?php echo $row->lastName; ?></td>
        <td><?php echo $row->email; ?></td>
    </tr>
<?php endwhile; ?>

That’s it. You can also learn dynamic pagination in Node JS and Mongo DB from here.

[wpdm_package id=’140′]