Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/abejarano/ts-mongo-criteria/llms.txt

Use this file to discover all available pages before exploring further.

This guide walks you through everything you need to go from zero to a running paginated query against MongoDB. By the end you will have a typed User aggregate, a UserRepository, a Criteria query, and a printed Paginate<User> result — all in less than five minutes.
1
Install the package
2
npm
npm install @abejarano/ts-mongodb-criteria
yarn
yarn add @abejarano/ts-mongodb-criteria
pnpm
pnpm add @abejarano/ts-mongodb-criteria
bun
bun add @abejarano/ts-mongodb-criteria
3
The mongodb peer dependency must also be present in your project:
4
npm install mongodb@^6.0.0
5
Set the MONGO_URI environment variable
6
The library’s MongoClientFactory reads a single environment variable to open its connection. Create (or update) a .env file in your project root:
7
# Local MongoDB
MONGO_URI=mongodb://localhost:27017/mydb

# MongoDB Atlas
MONGO_URI=mongodb+srv://user:password@cluster0.abc123.mongodb.net/mydb
8
MONGO_URI is the only required environment variable. If it is missing when any repository method is first called, the library throws: "MONGO_URI environment variables are missing to connect to the MongoDB server". Load the variable with a library such as dotenv before importing from @abejarano/ts-mongodb-criteria.
9
Define a domain entity
10
Extend AggregateRoot and implement the three required members: getId(), toPrimitives(), and the static fromPrimitives(). The repository uses fromPrimitives to hydrate documents retrieved from MongoDB back into typed class instances.
11
import { AggregateRoot } from "@abejarano/ts-mongodb-criteria";

export class User extends AggregateRoot {
  constructor(
    private readonly id: string,
    private readonly name: string,
    private readonly email: string,
    private readonly status: string,
    private readonly age: number
  ) {
    super();
  }

  /** Returns the document identifier used as MongoDB's _id. */
  getId(): string {
    return this.id;
  }

  /** Serializes the aggregate to a plain object for persistence. */
  toPrimitives(): any {
    return {
      id: this.id,
      name: this.name,
      email: this.email,
      status: this.status,
      age: this.age,
    };
  }

  /** Deserializes a plain MongoDB document back into a User instance. */
  static override fromPrimitives(data: any): User {
    return new User(
      data.id,
      data.name,
      data.email,
      data.status,
      data.age
    );
  }

  // Domain getters
  getName(): string  { return this.name; }
  getEmail(): string { return this.email; }
  getStatus(): string { return this.status; }
  getAge(): number   { return this.age; }
}
12
Create a UserRepository
13
Extend MongoRepository<User>, implement the abstract collectionName() method to tell the library which MongoDB collection to target, and implement ensureIndexes() to declare indexes. Indexes are created lazily on the first access — use the collection argument inside ensureIndexes directly to avoid triggering a recursive call.
14
import { Collection } from "mongodb";
import { MongoRepository } from "@abejarano/ts-mongodb-criteria";
import { User } from "./User";

export class UserRepository extends MongoRepository<User> {
  constructor() {
    // Pass the class itself so the base repository can call fromPrimitives.
    super(User);
  }

  /** MongoDB collection name for this repository. */
  collectionName(): string {
    return "users";
  }

  /**
   * Called once per process lifetime the first time the collection is accessed.
   * Use the `collection` argument here — do NOT call this.collection() to avoid
   * infinite recursion.
   */
  protected async ensureIndexes(collection: Collection): Promise<void> {
    await collection.createIndex({ email: 1 }, { unique: true });
    await collection.createIndex({ status: 1 });
  }
}
15
MongoRepository provides the following ready-to-use public methods:
16
MethodDescriptionlist(criteria, fieldsToExclude?, tx?)Paginated query returning Paginate<T>one(filter, tx?)Fetch a single matching documentmany(filter, tx?)Fetch all matching documentsupsert(entity, tx?)Insert or update an aggregate by its iddelete(filter, options?, tx?)Delete matching documents
17
Build a Criteria query
18
A Criteria is composed of a Filters set, an Order, an optional limit, and an optional page number. Use Filters.fromValues with an array of Map objects — each map requires the keys "field", "operator", and "value".
19
import {
  Criteria,
  Filters,
  Order,
  Operator,
} from "@abejarano/ts-mongodb-criteria";

// Find active users aged 18 or over, newest first, 20 per page
export const activeAdultsCriteria = new Criteria(
  Filters.fromValues([
    new Map([
      ["field",    "status"],
      ["operator", Operator.EQUAL],
      ["value",    "active"],
    ]),
    new Map([
      ["field",    "age"],
      ["operator", Operator.GTE],
      ["value",    "18"],
    ]),
  ]),
  Order.desc("createdAt"),
  20, // limit — number of results per page
  1   // page  — 1-based page number
);
20
To fetch all documents in a collection without any filter conditions use Filters.none():
const allUsersCriteria = new Criteria(Filters.none(), Order.asc("name"));
21
Available Operator values:
22
OperatorMongoDB equivalentNotesEQUAL$eqExact matchNOT_EQUAL$neExclusionGT / GTE$gt / $gteNumeric/date rangesLT / LTE$lt / $lteNumeric/date rangesCONTAINS$regexCase-sensitive substringNOT_CONTAINS$not: $regexSubstring exclusionBETWEEN$gte + $lteInclusive range using { start, end }OR$orMulti-field OR using OrCondition[]IN$inMatch any value in an arrayNOT_IN$ninExclude any value in an array
23
Execute the query and read the result
24
import "dotenv/config"; // load MONGO_URI before the first import
import { UserRepository } from "./UserRepository";
import { activeAdultsCriteria } from "./listActiveAdults";
import type { Paginate } from "@abejarano/ts-mongodb-criteria";
import type { User } from "./User";

async function main() {
  const repo = new UserRepository();

  // list<D>() returns Paginate<D> — provide the generic to type the results
  const page: Paginate<User> = await repo.list<User>(activeAdultsCriteria);

  console.log(`Total matching users : ${page.count}`);
  console.log(`Results on this page : ${page.results.length}`);
  console.log(`Next page number     : ${page.nextPag ?? "none"}`);

  for (const user of page.results) {
    console.log(`  ${user.getName()} <${user.getEmail()}>`);
  }
}

main().catch(console.error);
25
The Paginate<T> shape returned by list():
26
type Paginate<T> = {
  nextPag: string | number | null; // next page number, or null on the last page
  count:   number;                 // total documents matching the filter
  results: Array<T>;               // hydrated entities for this page
};

What’s Next

  • Transactions — wrap multiple upsert / delete calls in MongoTransaction.run so they commit or roll back together.
  • OR operator — search across several fields in one filter using Operator.OR and an OrCondition[] value.
  • Field exclusion — pass an array of field names as the second argument to list() to strip sensitive or large fields from the projection.
  • IRepository interface — extend IRepository<T> in your own interfaces to keep method signatures in sync with the library’s base class.

Build docs developers (and LLMs) love