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.

ts-mongo-criteria is built on a lightweight Domain-Driven Design (DDD) foundation. At the centre of that foundation is the Aggregate Root — the single entry point into a cluster of related domain objects. Every entity you want to persist through MongoRepository must extend AggregateRoot. Alongside it, the library ships a small set of Value Object base classes (ValueObject<T>, StringValueObject, EnumValueObject<T>) that let you model typed, self-validating domain values rather than raw primitives.

AggregateRoot abstract class

export abstract class AggregateRoot {
  static fromPrimitives(_data: any): AggregateRoot {
    throw new Error("fromPrimitives must be implemented in subclasses")
  }
  abstract getId(): string | undefined
  abstract toPrimitives(): any
}

export type AggregateRootClass<T extends AggregateRoot> = {
  fromPrimitives(data: any): T
}

getId(): string | undefined

Return the entity’s unique string identifier. MongoRepository calls this when upserting a document — the returned value becomes the _id field in MongoDB.
getId(): string | undefined {
  return this.id.value   // e.g. a UUID string
}
Return undefined if your entity does not yet have an ID (e.g. before first save), but be aware that MongoRepository.upsert() will rely on this value to match existing documents.

toPrimitives(): any

Serialise the entity into a plain JavaScript object suitable for MongoDB insertion or update. All nested value objects must be unwrapped to their primitive values here.
toPrimitives() {
  return {
    _id:       this.id.value,
    name:      this.name.value,
    email:     this.email.value,
    role:      this.role.value,       // enum string
    createdAt: this.createdAt,        // Date
  }
}

static fromPrimitives(data: any): AggregateRoot

Factory method that reconstructs a domain entity from a raw MongoDB document. The base class throws by default — override it in every subclass. MongoRepository uses the AggregateRootClass<T> type (described below) to call this method when hydrating query results.
static fromPrimitives(data: any): User {
  return new User(
    new UserId(data._id),
    new UserName(data.name),
    new UserEmail(data.email),
    UserRole.fromValue(data.role),
    new Date(data.createdAt),
  )
}

Full User entity example

import {
  AggregateRoot,
  StringValueObject,
  EnumValueObject,
} from "@abejarano/ts-mongodb-criteria"

// --- Value Objects ---

class UserId extends StringValueObject {}

class UserName extends StringValueObject {}

class UserEmail extends StringValueObject {}

enum UserRoles { ADMIN = "admin", MEMBER = "member", GUEST = "guest" }

class UserRole extends EnumValueObject<UserRoles> {
  static fromValue(value: string): UserRole {
    if (!Object.values(UserRoles).includes(value as UserRoles)) {
      throw new Error(`Invalid role: ${value}`)
    }
    return new UserRole(value as UserRoles, Object.values(UserRoles))
  }

  protected throwErrorForInvalidValue(value: UserRoles): void {
    throw new Error(`"${value}" is not a valid UserRole`)
  }
}

// --- Aggregate Root ---

export class User extends AggregateRoot {
  constructor(
    private readonly id:        UserId,
    private readonly name:      UserName,
    private readonly email:     UserEmail,
    private readonly role:      UserRole,
    private readonly createdAt: Date,
  ) {
    super()
  }

  // Required by AggregateRoot
  getId(): string | undefined {
    return this.id.value
  }

  // Required by AggregateRoot
  toPrimitives() {
    return {
      _id:       this.id.value,
      name:      this.name.value,
      email:     this.email.value,
      role:      this.role.value,
      createdAt: this.createdAt,
    }
  }

  // Required by AggregateRoot (override the throwing default)
  static fromPrimitives(data: any): User {
    return new User(
      new UserId(data._id),
      new UserName(data.name),
      new UserEmail(data.email),
      UserRole.fromValue(data.role),
      new Date(data.createdAt),
    )
  }

  // Domain helpers
  isAdmin(): boolean {
    return this.role.value === UserRoles.ADMIN
  }
}

AggregateRootClass<T>

export type AggregateRootClass<T extends AggregateRoot> = {
  fromPrimitives(data: any): T
}
This utility type captures a class constructor shape — specifically, the static fromPrimitives method. Pass the class itself (not an instance) to MongoRepository so it knows how to hydrate raw MongoDB documents back into your domain type:
import { MongoRepository } from "@abejarano/ts-mongodb-criteria"
import { Collection } from "mongodb"
import { User } from "./User"

class UserRepository extends MongoRepository<User> {
  constructor() {
    super(User)   // ← AggregateRootClass<User>: User.fromPrimitives will be called
  }

  collectionName(): string {
    return "users"
  }

  protected async ensureIndexes(collection: Collection): Promise<void> {
    await collection.createIndex({ email: 1 }, { unique: true })
  }
}

IRepository<T> interface

All concrete repository classes in the library implement IRepository<T>. The interface defines five methods:
export interface IRepository<T extends AggregateRoot> {
  many(filter: object, transaction?: MongoTransaction): Promise<T[]>
  one(filter: object, transaction?: MongoTransaction): Promise<T | null>
  list<D>(
    criteria: Criteria,
    fieldsToExclude?: string[],
    transaction?: MongoTransaction
  ): Promise<Paginate<D>>
  upsert(entity: T, transaction?: MongoTransaction): Promise<void>
  delete(
    filter: object,
    options?: DeleteOptions,
    transaction?: MongoTransaction
  ): Promise<void>
}
MethodDescription
many(filter)Fetch all documents matching a raw MongoDB filter object. Returns a hydrated array of T.
one(filter)Fetch the first document matching a raw MongoDB filter. Returns T or null.
list<D>(criteria, fieldsToExclude?)Translate a Criteria instance into a MongoDB query, run it, and return a Paginate<D> with total count and current page results.
upsert(entity)Insert or update a document whose _id matches entity.getId(). Calls entity.toPrimitives() internally.
delete(filter, options?)Delete all documents matching a raw MongoDB filter. options accepts any standard MongoDB DeleteOptions (e.g. collation, hint).
All methods accept an optional MongoTransaction for session-scoped operations.

Value Objects

Value objects are immutable domain primitives that carry their own validation. Rather than passing raw strings and numbers between layers, you wrap them in a typed class that enforces invariants at construction time.

ValueObject<T>

The root base class for all value objects. Holds a single readonly value: T and throws InvalidArgumentError if the value is undefined.
import { ValueObject } from "@abejarano/ts-mongodb-criteria"

// Example: a typed price
class Price extends ValueObject<number> {
  constructor(value: number) {
    super(value)
    if (value < 0) throw new Error("Price cannot be negative")
  }
}

const price = new Price(9.99)
console.log(price.value)      // 9.99
console.log(price.toString()) // "9.99"

const other = new Price(9.99)
console.log(price.equals(other)) // true  — structural equality
Key methods:
MethodDescription
equals(other)true if both objects are the same class and have the same value.
toString()Returns value.toString().

StringValueObject

Extends ValueObject<string>. Adds a guard that rejects empty strings and provides a getValue(): string helper.
import { StringValueObject } from "@abejarano/ts-mongodb-criteria"

class ProductName extends StringValueObject {}
class UserEmail   extends StringValueObject {}

const name  = new ProductName("Wireless Keyboard")
const email = new UserEmail("user@example.com")

console.log(name.value)       // "Wireless Keyboard"
console.log(email.getValue()) // "user@example.com"

// new ProductName("") → throws InvalidArgumentError: "String should have a length"

EnumValueObject<T>

Extends the enum pattern — holds a value: T and validates it against a list of validValues at construction time. Subclasses must implement throwErrorForInvalidValue().
import { EnumValueObject } from "@abejarano/ts-mongodb-criteria"

enum OrderStatus { PENDING = "pending", PAID = "paid", CANCELLED = "cancelled" }

class OrderStatusVO extends EnumValueObject<OrderStatus> {
  constructor(value: OrderStatus) {
    super(value, Object.values(OrderStatus))
  }

  protected throwErrorForInvalidValue(value: OrderStatus): void {
    throw new Error(`"${value}" is not a valid OrderStatus`)
  }

  static fromValue(raw: string): OrderStatusVO {
    return new OrderStatusVO(raw as OrderStatus) // validation runs in super()
  }

  isPending():   boolean { return this.value === OrderStatus.PENDING }
  isCancelled(): boolean { return this.value === OrderStatus.CANCELLED }
}

const status = OrderStatusVO.fromValue("paid")
console.log(status.value)        // "paid"
console.log(status.isPending())  // false

// OrderStatusVO.fromValue("refunded")
// → throws: "refunded" is not a valid OrderStatus
EnumValueObject is used internally by OrderType and FilterOperator, and is the recommended base for any domain enum that needs validation.

Build docs developers (and LLMs) love