CRUD Operations
Monch provides a clean API for all database operations with automatic validation and type safety.
Create
insertOne
Insert a single document with full Zod validation:
const user = await Users.insertOne({
name: 'Alice',
email: 'alice@example.com',
});
// Returns the inserted document with _id and timestampsinsertMany
Insert multiple documents:
const users = await Users.insertMany([
{ name: 'Alice', email: 'alice@example.com' },
{ name: 'Bob', email: 'bob@example.com' },
]);
// Returns array of inserted documentsEach document is validated individually. If any document fails validation, the entire operation is rejected.
Read
find
Find multiple documents:
// Basic query
const users = await Users.find({ role: 'admin' }).toArray();
// With options
const recentUsers = await Users.find({ createdAt: { $gte: lastWeek } })
.sort({ createdAt: -1 })
.limit(10)
.toArray();
// Empty filter returns all documents
const allUsers = await Users.find().toArray();findOne
Find a single document:
const user = await Users.findOne({ email: 'alice@example.com' });
// Returns document or nullfindById
Find by ObjectId (accepts string or ObjectId):
const user = await Users.findById('507f1f77bcf86cd799439011');
// or
const user = await Users.findById(new ObjectId('507f1f77bcf86cd799439011'));count
Count matching documents:
const count = await Users.count({ role: 'admin' });estimatedDocumentCount
Fast count using collection metadata (doesn't filter):
const total = await Users.estimatedDocumentCount();exists
Check if any documents match:
const hasAdmins = await Users.exists({ role: 'admin' });
// Returns booleandistinct
Get distinct values for a field:
const roles = await Users.distinct('role');
// ['user', 'admin', 'moderator']
// With filter
const activeRoles = await Users.distinct('role', { status: 'active' });Update
updateOne
Update a single document:
const user = await Users.updateOne(
{ email: 'alice@example.com' },
{ $set: { role: 'admin' } }
);
// Returns updated document or nullPartial Validation: Only fields in $set and $setOnInsert are validated against the schema.
updateMany
Update multiple documents:
const count = await Users.updateMany(
{ role: 'user' },
{ $set: { verified: true } }
);
// Returns number of modified documentsUpsert
Insert if not found, update if exists:
const user = await Users.updateOne(
{ email: 'alice@example.com' },
{
$set: { lastLogin: new Date() },
$setOnInsert: { name: 'Alice', role: 'user' },
},
{ upsert: true }
);Upsert Validation
For updateOne / findOneAndUpdate with upsert: true, Monch uses partial validation - only fields in $set and $setOnInsert are validated, not the entire document. This allows incremental document creation.
For replaceOne / findOneAndReplace with upsert: true, Monch uses full validation - the entire replacement document must satisfy the schema.
See Validation for details.
Replace
Replace an entire document:
const result = await Users.replaceOne(
{ _id: user._id },
{ name: 'Alice Smith', email: 'alice@example.com', role: 'admin' }
);
// Returns UpdateResultThe replacement document is fully validated against the schema.
Delete
deleteOne
Delete a single document:
const deleted = await Users.deleteOne({ email: 'alice@example.com' });
// Returns boolean (true if deleted)deleteMany
Delete multiple documents:
const count = await Users.deleteMany({ status: 'inactive' });
// Returns number of deleted documentsAtomic Operations
These operations are atomic—they find and modify in a single operation.
findOneAndUpdate
const user = await Users.findOneAndUpdate(
{ email: 'alice@example.com' },
{ $inc: { loginCount: 1 } },
{ returnDocument: 'after' } // Return updated document
);findOneAndDelete
const deletedUser = await Users.findOneAndDelete(
{ email: 'alice@example.com' }
);
// Returns the deleted documentfindOneAndReplace
const user = await Users.findOneAndReplace(
{ _id: userId },
{ name: 'Alice', email: 'alice@example.com', role: 'admin' },
{ returnDocument: 'after' }
);No Hooks
Atomic operations (findOneAndUpdate, findOneAndDelete, findOneAndReplace) do not trigger lifecycle hooks.
Bulk Operations
Execute multiple operations in a single request:
const result = await Users.bulkWrite([
{ insertOne: { document: { name: 'Bob', email: 'bob@example.com' } } },
{ updateOne: { filter: { _id: id1 }, update: { $set: { name: 'Robert' } } } },
{ deleteOne: { filter: { _id: id2 } } },
{ replaceOne: { filter: { _id: id3 }, replacement: { name: 'Carol', email: 'carol@example.com' } } },
]);Each operation is validated according to its type:
insertOne: Full validationupdateOne/updateMany: Partial validationreplaceOne: Full validation
No Hooks
bulkWrite uses Zod-only validation and does not trigger any lifecycle hooks.
Aggregation
Run aggregation pipelines:
const results = await Users.aggregate([
{ $match: { status: 'active' } },
{ $group: { _id: '$role', count: { $sum: 1 } } },
{ $sort: { count: -1 } },
]);
// Returns AggregationCursor - use .toArray() to get results
const data = await results.toArray();Session Support
All operations accept an options object with session for transactions:
await Users.insertOne(
{ name: 'Alice', email: 'alice@example.com' },
{ session }
);
await Users.updateOne(
{ email: 'alice@example.com' },
{ $set: { role: 'admin' } },
{ session }
);See Transactions for more details.