@abejarano/ts-mongodb-criteria
v1.10.0
Published
Patrón Criteria para consultas MongoDB en TypeScript
Maintainers
Readme
🔍 TypeScript MongoDB Criteria Pattern
A robust, type-safe implementation of the Criteria Pattern for MongoDB queries in TypeScript. Build complex database queries dynamically with a fluent, composable API designed following Domain-Driven Design (DDD) and Clean Architecture principles.
📚 Table of Contents
- 🎯 Overview
- ✨ Key Features
- 📦 Installation
- 🚀 Quick Start
- 🗄️ Migrations
- 📖 Documentation
- 🧪 Testing
- 🤝 Contributing
- 📄 License
🎯 Overview
The Criteria Pattern is a powerful design pattern that enables dynamic query construction without writing raw database queries. This library provides a type-safe, MongoDB-specific implementation that helps you:
- Build queries dynamically based on runtime conditions
- Maintain type safety throughout your query construction
- Compose and reuse query components across your application
- Separate concerns between query logic and data models
- Test queries easily with a mockable interface
What is the Criteria Pattern?
The Criteria pattern encapsulates query logic in a structured, object-oriented way. Instead of building query strings or objects directly, you compose Criteria objects that represent your search intentions. This approach provides flexibility, reusability, testability, and type safety.
✨ Key Features
🔒 Type Safety First
- Full TypeScript support with strict typing
- Compile-time validation of query structure
- IntelliSense support for all operations
🧩 Flexible Query Building
- Support for all common MongoDB operators (EQUAL, NOT_EQUAL, GT, GTE, LT, LTE, CONTAINS, NOT_CONTAINS)
- NEW: OR operator for complex logical combinations
- Composable filters that can be combined and reused
- Dynamic query construction based on runtime conditions
📊 Advanced Querying
// Simple equality
{ status: { $eq: "active" } }
// Complex OR conditions
{ $or: [
{ name: { $regex: "john" } },
{ email: { $regex: "john" } }
]}
// Range queries
{ age: { $gte: 18 }, price: { $lte: 999.99 } }🎯 MongoDB Optimized
- Native MongoDB 6.0+ support
- Efficient query generation
- Automatic index-friendly query structure
📦 Zero Dependencies
- Only peer dependencies (MongoDB driver)
- Lightweight bundle size
🏗️ Clean Architecture
- Repository pattern implementation
- Domain-driven design principles
- Separation of concerns
📦 Installation
# Using npm
npm install @abejarano/ts-mongodb-criteria
# Using yarn
yarn add @abejarano/ts-mongodb-criteria
# Using pnpm
pnpm add @abejarano/ts-mongodb-criteriaPackage Manager
This repo is Bun-first and ships with a bun.lock. Use Bun for installs and
scripts:
bun installPeer Dependencies
# MongoDB driver (required)
npm install mongodb@^6.0.0
# TypeScript (for development)
npm install -D typescript@^5.0.0System Requirements
- Node.js: 20.0.0 or higher
- TypeScript: 5.0.0 or higher (for development)
- MongoDB: 6.0.0 or higher
🚀 Quick Start
import {
Criteria,
Filters,
Order,
Operator,
MongoRepository,
} from "@abejarano/ts-mongodb-criteria"
// 1. Create filters using a simple Map-based syntax
const filters = [
new Map([
["field", "status"],
["operator", Operator.EQUAL],
["value", "active"],
]),
new Map([
["field", "age"],
["operator", Operator.GTE],
["value", "18"],
]),
]
// 2. Build criteria with filters, sorting, and pagination
const criteria = new Criteria(
Filters.fromValues(filters),
Order.desc("createdAt"),
20, // limit
1 // page
)
// 3. Use with your MongoDB repository
class UserRepository extends MongoRepository<User> {
constructor() {
super(User)
}
collectionName(): string {
return "users"
}
// Create indexes the first time the collection is accessed
protected async ensureIndexes(collection: Collection): Promise<void> {
await collection.createIndex({ email: 1 }, { unique: true })
}
}
const userRepo = new UserRepository()
const { results } = await userRepo.list(criteria)Indexes are created lazily the first time the repository accesses the collection
(via list, one, or upsert).
Use the collection argument inside ensureIndexes to avoid recursion.
MongoRepository provides ready-to-use public methods for repositories that extend it:
list(criteria, fieldsToExclude?)for paginated queriesone(filter)to fetch a single entityupsert(entity)to persist an aggregate Internal helpers are private, so repositories should call these public methods directly.
If you need a repository interface in your app, extend IRepository<T> so your
custom interfaces stay aligned with the library return types:
import type { IRepository } from "@abejarano/ts-mongodb-criteria"
export interface IUserRepository extends IRepository<User> {
// Add domain-specific methods here
}The goal of IRepository is to prevent signature drift (e.g. upsert returning
void in your app while the base repository returns ObjectId | null).
Atomic Transactions
MongoTransaction.run groups writes from one or more repositories into a
single MongoDB transaction. Pass the tx context to every write that must be
committed or rolled back together:
import { MongoTransaction } from "@abejarano/ts-mongodb-criteria"
await MongoTransaction.run(async (tx) => {
await userRepository.upsert(user, tx)
await auditRepository.upsert(auditEntry, tx)
await sessionRepository.delete({ userId: user.getId() }, undefined, tx)
// Reads can observe writes made earlier in this transaction.
const persistedUser = await userRepository.one({ id: user.getId() }, tx)
})If any operation in the callback fails, MongoDB rolls back all the writes and
the error is propagated to the caller. The MongoDB driver handles retryable
transaction errors through withTransaction.
Custom repositories can include their protected atomic updates in the same
transaction by accepting and forwarding MongoTransaction:
async deactivateUser(id: string, tx: MongoTransaction): Promise<void> {
await this.updateOne({ id }, { $set: { active: false } }, tx)
}MongoDB transactions require a replica set or a sharded cluster; standalone
MongoDB instances do not support them. This initial API applies the transaction
context to upsert, delete, protected updateOne, one, and list.
Pass tx as the third argument to list after fieldsToExclude.
Your First Query in 30 Seconds:
// Find active users over 18, sorted by creation date
const activeAdultUsers = 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"),
10, // Get 10 results
1 // First page
)
const results = await repository.list(activeAdultUsers)🗄️ Migrations
This repo includes a CLI wrapper for migrate-mongo.
To use it in your application:
- Initialize the configuration:
bun ts-mongo initEdit
migrate-mongo-config.jswith your database details.Run migrations using the
ts-mongocommand:
# Create a new migration
bun ts-mongo migrate:create add-users-index
# Run migrations up
bun ts-mongo migrate:up
# Undo last migration
bun ts-mongo migrate:down
# Check status
bun ts-mongo migrate:statusNote: You can also use npx, bunx or yarn run.
See the full CLI guide here: docs/mongo-migrations.md.
📖 Documentation
📖 Complete Documentation
- 📘 Quick Start Guide - Get up and running in minutes
- 🏗️ Criteria Pattern Guide - Deep dive into the pattern, architecture, and theory
- 🔧 Operators Reference - Complete guide to all available operators and their usage
- ⚡ Performance Guide - Optimization strategies and best practices
- 🔄 Migration Guide - Migrate from other query systems to Criteria pattern
- 🗄️ MongoDB Migrations (CLI) - Run database migrations with migrate-mongo
🎯 Key Concepts
1. Criteria - The main query builder
const criteria = new Criteria(filters, order, limit?, page?)2. Filters - Collection of filter conditions
const filters = Filters.fromValues([...filterMaps])3. Order - Sorting specification
const order = Order.desc("createdAt") // or Order.asc("name")4. Operators - Available filter operations
EQUAL,NOT_EQUAL- Exact matchingGT,GTE,LT,LTE- Range operationsBETWEEN- Inclusive range with lower and upper boundsCONTAINS,NOT_CONTAINS- Text searchOR- Logical OR combinations
✅ Runtime Compatibility
This library ships dual builds (CJS + ESM) and works in both Node.js and Bun.
🆕 OR Operator Example
import { OrCondition } from "@abejarano/ts-mongodb-criteria"
// Search across multiple fields
const searchConditions: OrCondition[] = [
{ field: "name", operator: Operator.CONTAINS, value: "john" },
{ field: "email", operator: Operator.CONTAINS, value: "john" },
]
const filters = [
new Map([
["field", "search"],
["operator", Operator.OR],
["value", searchConditions],
]),
]
// Generates: { $or: [
// { name: { $regex: "john" } },
// { email: { $regex: "john" } }
// ]}⏱️ BETWEEN Operator Example
// Filter users created between two dates
const filters = [
new Map([
["field", "createdAt"],
["operator", Operator.BETWEEN],
["value", { start: new Date("2024-01-01"), end: new Date("2024-01-31") }],
]),
]
const criteria = new Criteria(Filters.fromValues(filters), Order.none())
// Generates: { createdAt: { $gte: 2024-01-01, $lte: 2024-01-31 } }🧪 Testing
The library includes comprehensive test coverage (30/30 tests passing).
# Run all tests
npm test
# Run tests in watch mode
npm run test:watch
# Run tests with coverage
npm run test:coverage🤝 Contributing
We welcome contributions! Please follow these guidelines:
Development Setup
# Clone the repository
git clone https://github.com/abejarano/ts-mongo-criteria.git
cd ts-mongo-criteria
# Install dependencies
yarn install
# Run tests
yarn test
# Build the project
yarn buildContribution Process
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Write tests for your changes
- Ensure all tests pass (
yarn test) - Commit using conventional commits (
git commit -m 'feat: add amazing feature') - Push to your branch (
git push origin feature/amazing-feature) - Open a Pull Request
CLI & Migrations
To use the migration features, you must first initialize the configuration in your project:
bun run ts-mongo initThis will create a migrate-mongo-config.js file in your project root. You must configure your MongoDB connection settings in this file (or via environment variables as defined in the config).
Commands
| Command | Description |
| ----------------------- | --------------------------------- |
| init | Initialize configuration file |
| migrate:create <name> | Create a new migration file |
| migrate:up | Run pending migrations |
| migrate:down | Revert the last applied migration |
| migrate:status | Check the status of migrations |
Example:
bun run ts-mongo migrate:create add-users-collection📄 License
This project is licensed under the MIT License. See the LICENSE file for details.
👨💻 Author
Ángel Bejarano
📧 [email protected]
🐙 GitHub
🏢 Jaspesoft
⭐️ If this project helps you, please give it a star on GitHub!
🤝 Questions or suggestions? Open an issue or start a discussion.
📢 Follow us for updates and new features!
