bhylu.com

Advertisements

Node.js MongoDB Pagination

A MongoDB cursor represents a query result that is consumed over time. Set ordering and bounds before materializing a small result array.

How it works

The example uses unique increasing numeric ids to make a simple keyset page understandable. It does not claim that skip and limit alone produce a stable view while records are changing. A production cursor token should be tied to a known sort order and validated before its values enter a fixed filter. The returned record ids are data, not permissions to read every matching document.

Run the code

Run this example locally with Node.js. The exact sources are reviewed on Node 22.15.1 and Node 24.22.1 unless a specialised lab states otherwise. Save the complete source as mongo-pagination.mjs in a separate empty exercises folder and run node mongo-pagination.mjs. There is no Node.js browser runner on Bhylu; the plain JavaScript playground does not provide these filesystem, server or process APIs. In that separate project, install the reviewed packages with npm install --save-exact mongodb@6.17.0. These are teaching dependencies, not changes to the Bhylu website package tree. This database example is reviewed on Node 22.15.1 against its named real database lab. Do not run it against an existing application database. Read the dedicated lab requirements below before execution.

Download the complete source. Ordinary Node scripts need no Apache, PHP or MySQL service. File examples own their temporary folders; network fixtures use loopback and available temporary ports.

Dedicated MongoDB lab

Use MongoDB Community 8.0.12 and mongodb driver 6.17.0 in a disposable authenticated loopback instance. The script requires BHYLU_NODE_DB_LAB=YES and BHYLU_MONGO_URI, including the lab host, port, authentication database and your private generated credentials. It creates a random bhylu_node_ database, inserts fictional records and drops only that database in finally. Do not use an Atlas or existing application URI. The review is a standalone instance, not a replica-set transaction, sharding or failover test.

Example

mongo-pagination.mjs

import { MongoClient } from 'mongodb';
import { randomBytes } from 'node:crypto';
const uri = process.env.BHYLU_MONGO_URI;
let endpoint;
try {
  endpoint = new URL(uri);
} catch {
  throw new Error('Use the dedicated loopback MongoDB lab');
}
if (
  process.env.BHYLU_NODE_DB_LAB !== 'YES' ||
  endpoint.protocol !== 'mongodb:' ||
  endpoint.hostname !== '127.0.0.1' ||
  !endpoint.port ||
  endpoint.port === '27017'
)
  throw new Error('Use the dedicated loopback MongoDB lab');
const client = new MongoClient(uri, { serverSelectionTimeoutMS: 5000 });
await client.connect();
const database = client.db('bhylu_node_' + randomBytes(8).toString('hex'));
try {
  const kits = database.collection('kits');
  await kits.insertMany([
    { _id: 701, name: 'tripod', available: 3 },
    { _id: 702, name: 'binoculars', available: 2 },
    { _id: 703, name: 'torch', available: 0 },
  ]);
  const rows = await kits
    .find({ _id: { $gt: 701 } }, { projection: { _id: 1 } })
    .sort({ _id: 1 })
    .limit(2)
    .toArray();
  console.log(rows.map((row) => row._id).join(','));
  const available = await kits
    .find({ available: { $gt: 0 } })
    .sort({ available: -1, _id: 1 })
    .limit(1)
    .toArray();
  console.log(available[0]._id);
} finally {
  try {
    await database.dropDatabase();
  } finally {
    await client.close();
  }
}

Expected result

702,703
701

The id cursor and availability ranking are distinct query contracts. Each has a deterministic tie-break and a small result bound. Index choice, execution plan and actual data distribution still determine performance; this fixture is not a benchmark.

Use the Node.js API reference for the precise version contract.

Advertisements