What is mongoose-paginate-v2?

mongoose-paginate-v2 is a pagination library for mongoose. With mongoose-paginate-v2, you can fetch a list of documents from a MongoDB collection and get a paginated response.

Paginated responses are useful for cases where UI clients needs to query and display only a section of data at a time.

UI Table Pagination - ui.shadcn.com

UI Table Pagination - ui.shadcn.com

The mongoose-paginate-v2 documentation provides a code snippet for how to use the library with TypeScript. 1

Setting the type of docs items

Prior to version 1.7.4 of mongoose-paginate-v2, it was not possible to set the type of the items in docs. The type of the items in docs was set to the type passed in when the mongoose model was created.

import { model, PaginateModel, Schema, Types } from "mongoose";
import paginate from 'mongoose-paginate-v2';

// Create the model interface
interface Person {
  _id: Types.ObjectId
  name: string
  birthYear: number
  favouriteFood: string
}

// Create the schema
const personSchema = new mongoose.Schema({
  name: String;
  birthYear: Number;
  favouriteFood: String;
 });

// attach the mongoose-paginate-v2 plugin
personSchema.plugin(paginate);

// create the paginated model
// PaginateModel<Person> sets the type of docs items to Person
const PersonModel = model<Person, PaginateModel<Person>>('Person', personSchema);

// Create documents
await PersonModel.create([
  {
    name: "John Doe",
    birthYear: 1970
    favouriteFood: "Bacon"
  },
  {
    name: "Jane Doe"
    birthYear: 1970
    favouriteFood: "French fries"
  }
])

// Get a paginated list of documents
const { docs, totalDocs, page, prevPage, nextPage, totalPages } =
  await Business.paginate(
    {},
    {
      offset: 1,
      limit: 1,
    }
  );

In the example above, the items in the docs array will be typed to Person, and docs will have a type of Person[]. Everything is fine until you use the select, the populate or the other mongoose options that change the output like projection and fields.

const { docs, totalDocs, page, prevPage, nextPage, totalPages } =
  await Business.paginate(
    {},
    {
      select: "-favouriteFood",
      offset: 1,
      limit: 1,
    }
  );

In the code snippet above, we exclude the favouriteFood field from the items in the docs fields returned. In other words, the type of docs should be an array of

{
  _id: Types.ObjectId;
  name: string;
  birthYear: number;
}

but it is not so. docs is typed to Person[] with all the fields included, which can be deceiving.

Setting Custom Types for docs Items

mongoose-paginate-v2 version 1.7.4 now provides an option that can be used to dynamically set the type of docs items.

Setting the type of the items returned in the docs array can be done in either of two ways.

1. Passing the desired type as the first argument to the paginate generic

type PersonWithoutFavouriteFood = {
  _id: Types.ObjectId;
  name: string;
  birthYear: number;
};

const { docs, totalDocs, page, prevPage, nextPage, totalPages } =
  await Business.paginate<PersonWithoutFavouriteFood>(
    {},
    {
      select: "-favouriteFood",
      offset: 1,
      limit: 1,
    }
  );

You can use this method is you do not want to modify the type of the options passed in as the second argument to the paginate function.

2. Passing the desired type as the second argument to the paginate generic

import { PaginateOptions } from "mongoose";

type PersonWithoutFavouriteFood = {
  _id: Types.ObjectId;
  name: string;
  birthYear: number;
};

const { docs, totalDocs, page, prevPage, nextPage, totalPages } =
  await Business.paginate<PaginateOptions, PersonWithoutFavouriteFood>(
    {},
    {
      select: "-favouriteFood",
      offset: 1,
      limit: 1,
    }
  );

You can use this method if you want to modify the type of the options passed in as the second argument to the paginate function. The type is PaginateOptions by default.

Shameless plug - I authored this feature.