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 typedDocumentation 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.
User
aggregate, a UserRepository, a Criteria query, and a printed
Paginate<User> result — all in less than five minutes.
The library’s
MongoClientFactory reads a single environment variable to open
its connection. Create (or update) a .env file in your project root:# Local MongoDB
MONGO_URI=mongodb://localhost:27017/mydb
# MongoDB Atlas
MONGO_URI=mongodb+srv://user:password@cluster0.abc123.mongodb.net/mydb
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.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.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; }
}
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.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 });
}
}
list(criteria, fieldsToExclude?, tx?)Paginate<T>one(filter, tx?)many(filter, tx?)upsert(entity, tx?)delete(filter, options?, tx?)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".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
);
EQUAL$eqNOT_EQUAL$neGT / GTE$gt / $gteLT / LTE$lt / $lteCONTAINS$regexNOT_CONTAINS$not: $regexBETWEEN$gte + $lte{ start, end }OR$orOrCondition[]IN$inNOT_IN$ninimport "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);
What’s Next
- Transactions — wrap multiple
upsert/deletecalls inMongoTransaction.runso they commit or roll back together. - OR operator — search across several fields in one filter using
Operator.ORand anOrCondition[]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.