Skip to content

Querying ​

Monch provides a chainable cursor API for building queries with full type safety.

Basic Queries ​

typescript
// Find all
const users = await Users.find().toArray();

// Find with filter
const admins = await Users.find({ role: 'admin' }).toArray();

// Find one
const user = await Users.findOne({ email: 'alice@example.com' });

// Find by ID (accepts string or ObjectId)
const user = await Users.findById('507f1f77bcf86cd799439011');

Chainable Cursor ​

The find() method returns a Cursor that supports method chaining:

typescript
const users = await Users.find({ status: 'active' })
  .sort({ createdAt: -1 })
  .skip(20)
  .limit(10)
  .project({ name: 1, email: 1 })
  .toArray();

sort ​

Sort results by one or more fields:

typescript
// Single field
.sort({ createdAt: -1 })  // Descending

// Multiple fields
.sort({ role: 1, name: 1 })  // Ascending

skip ​

Skip a number of documents:

typescript
.skip(20)  // Skip first 20 documents

limit ​

Limit the number of results:

typescript
.limit(10)  // Return at most 10 documents

project ​

Select specific fields:

typescript
// Include fields
.project({ name: 1, email: 1 })

// Exclude fields
.project({ password: 0, secret: 0 })

Executing Queries ​

toArray ​

Execute and return results as an array:

typescript
const users = await Users.find({ role: 'admin' }).toArray();
// Returns MonchArray<User> (array with .serialize() method)

The returned array has a .serialize() method for converting all documents:

typescript
const users = await Users.find({ role: 'admin' }).toArray();
const plain = users.serialize();
// All documents serialized for JSON/Next.js

serialize ​

Execute and serialize in one step:

typescript
const users = await Users.find({ role: 'admin' }).serialize();
// Returns Serialized<User>[] - ready for JSON/Next.js

This is equivalent to .toArray() followed by .serialize().

paginate ​

Execute with pagination metadata:

typescript
const result = await Users.find({ status: 'active' })
  .sort({ createdAt: -1 })
  .paginate({ page: 2, limit: 20 });

Returns:

typescript
{
  data: User[],           // Array of documents
  pagination: {
    page: number,         // Current page (1-based)
    limit: number,        // Items per page
    total: number,        // Total matching documents
    totalPages: number,   // Total number of pages
    hasNext: boolean,     // Has more pages
    hasPrev: boolean,     // Has previous pages
  }
}

Options:

  • page - Page number (default: 1)
  • limit - Items per page (default: 20, silently capped at 100)

MongoDB Query Operators ​

Monch supports all MongoDB query operators:

Comparison ​

typescript
// Equal
{ age: 25 }
{ age: { $eq: 25 } }

// Not equal
{ status: { $ne: 'deleted' } }

// Greater than / less than
{ age: { $gt: 18 } }
{ age: { $gte: 18 } }
{ age: { $lt: 65 } }
{ age: { $lte: 65 } }

// In array
{ role: { $in: ['admin', 'moderator'] } }
{ role: { $nin: ['banned', 'suspended'] } }

Logical ​

typescript
// AND (implicit)
{ role: 'admin', status: 'active' }

// AND (explicit)
{ $and: [{ role: 'admin' }, { status: 'active' }] }

// OR
{ $or: [{ role: 'admin' }, { isOwner: true }] }

// NOR
{ $nor: [{ status: 'deleted' }, { status: 'banned' }] }

// NOT
{ age: { $not: { $lt: 18 } } }

Element ​

typescript
// Field exists
{ deletedAt: { $exists: false } }

// Type check
{ age: { $type: 'number' } }

Array ​

typescript
// Contains element
{ tags: 'javascript' }

// Contains all elements
{ tags: { $all: ['javascript', 'typescript'] } }

// Array size
{ tags: { $size: 3 } }

// Element match
{ comments: { $elemMatch: { author: 'Alice', score: { $gt: 5 } } } }
typescript
// Requires text index
{ $text: { $search: 'mongodb tutorial' } }

Regex ​

typescript
// Case-insensitive search
{ name: { $regex: /alice/i } }
{ name: { $regex: 'alice', $options: 'i' } }

Nested Fields ​

Query nested fields using dot notation:

typescript
// Nested object field
{ 'profile.bio': { $exists: true } }
{ 'address.city': 'New York' }

// Array element field
{ 'comments.0.author': 'Alice' }

// Any array element
{ 'comments.author': 'Alice' }

MonchFilter Type ​

Monch uses a relaxed filter type (MonchFilter<T>) that works better with Zod-inferred types:

typescript
// All of these work without type casting:
Users.find({ email: 'test@example.com' });           // Known field
Users.find({ createdAt: { $gte: new Date() } });     // Timestamp field
Users.find({ 'profile.name': 'John' });              // Nested path
Users.find({ $or: [{ status: 'active' }, { role: 'admin' }] });

You typically don't need to import MonchFilter directly—all collection methods use it internally.

Performance Tips ​

  1. Use indexes for frequently queried fields
  2. Limit results to avoid loading too much data
  3. Project only needed fields to reduce transfer size
  4. Use exists() instead of count() > 0 for existence checks
  5. Use estimatedDocumentCount() when you don't need exact counts

Released under the MIT License.