easy-mongoo
v2.0.4
Published
Ultra-simple MongoDB wrapper with all mongoose features in easy syntax
Maintainers
Readme
Easy-Mongoo 🚀
Ultra-simple MongoDB wrapper with all Mongoose features in easy syntax. Make MongoDB operations effortless while maintaining enterprise-grade power and flexibility.
✨ Features
- 🎯 One-Line Operations - Perform complex database operations with minimal code
- 🛡️ Auto Error Handling - Automatic error handling with user-friendly messages
- 🔧 Smart Schema System - Create schemas with simple shortcuts and auto-features
- ⚡ All Mongoose Features - Virtuals, middleware, transactions, aggregation - everything included!
- 📦 Zero Configuration - Start coding immediately with sensible defaults
- 🎨 Predefined Templates - Ready-to-use schema templates for common use cases
- 🔐 Built-in Validation - Smart validation with friendly error messages
- 🚄 High Performance - Optimized for speed without sacrificing functionality
📦 Installation
npm install easy-mongoo🚀 Quick Start
Get up and running in just 3 steps:
const mongoo = require('easy-mongoo');
// 1. Connect to MongoDB
await mongoo.connect('mongodb://localhost:27017/mydb');
// 2. Create a model with easy schema
mongoo.model('User', {
name: 'string!',
email: 'email!!',
age: 'number?'
});
// 3. Use it!
const user = await mongoo.create('User', {
name: 'John Doe',
email: '[email protected]',
age: 30
});
console.log(user);That's it! You've just created a MongoDB connection, defined a schema, and saved a document.
📖 Table of Contents
- Easy-Mongoo 🚀
- ✨ Features
- 📦 Installation
- 🚀 Quick Start
- 📖 Table of Contents
- 🔌 Connection
- 📝 Schema Creation
- 🎨 Models
- 🔨 CRUD Operations
- 🆔 ID-Based Operations
- 📦 Batch Operations
- 🔧 Field-Specific Updates
- 🔄 Status Operations
- 🗑️ Soft Delete Operations
- 🎭 Virtual Fields
- 🔧 Methods & Statics
- 🪝 Middleware (Hooks)
- 💳 Transactions
- 📊 Aggregation
- 🔍 Indexes
- 🔌 Plugins
- 🌟 Advanced Features
- 🎯 Predefined Templates
- ❌ Error Handling
- 🐛 Debugging
- 🧰 Utility Methods
- 🆚 Mongoose vs Easy-Mongoo
- 📚 Complete API Reference
- 🤝 Contributing
- 📄 License
- 🙏 Acknowledgments
- 📞 Support
- 🗺️ Roadmap
🔌 Connection
Connect to MongoDB with automatic error handling:
// Basic connection
await mongoo.connect('mongodb://localhost:27017/mydb');
// With options and debug mode
await mongoo.connect('mongodb://localhost:27017/mydb', {
debug: true,
maxPoolSize: 10,
minPoolSize: 2
});
// Check connection status
const status = mongoo.status();
console.log(status);
// {
// connected: true,
// readyState: 'connected',
// database: 'mydb',
// host: 'localhost',
// port: 27017,
// models: ['User', 'Post'],
// collections: ['users', 'posts'],
// plugins: 2,
// cache: 5
// }
// Disconnect when done
await mongoo.disconnect();📝 Schema Creation
Easy-Mongoo provides an ultra-simple schema system with smart shortcuts:
Basic Schema Shortcuts
const userSchema = {
// Required string
name: 'string!',
// Optional number
age: 'number?',
// Boolean with default value
isActive: 'boolean+',
// Array of strings
tags: ['string'],
// Object with nested properties
address: {
street: 'string!',
city: 'string!',
country: 'string!'
}
};Schema Shortcuts Reference
| Shortcut | Description | Equivalent To |
|---------------|------------------------------|------------------------------------------------------------|
| 'string' | Basic string | String |
| 'string!' | Required string | { type: String, required: true } |
| 'string+' | String with default | { type: String, default: '' } |
| 'string!!' | Required & unique string | { type: String, required: true, unique: true } |
| 'number' | Basic number | Number |
| 'number!' | Required number | { type: Number, required: true } |
| 'number+' | Number with default 0 | { type: Number, default: 0 } |
| 'boolean' | Basic boolean | Boolean |
| 'boolean!' | Required boolean | { type: Boolean, required: true } |
| 'boolean+' | Boolean with default false | { type: Boolean, default: false } |
| 'date' | Basic date | Date |
| 'date+' | Date with default now | { type: Date, default: Date.now } |
| 'array' | Basic array | Array |
| 'array+' | Array with default [] | { type: Array, default: [] } |
| 'object' | Basic object | Object |
| 'object+' | Object with default {} | { type: Object, default: {} } |
| 'email' | Email with validation | { type: String, lowercase: true, match: emailRegex } |
| 'email!!' | Required unique email | { type: String, required: true, unique: true, lowercase: true, match: emailRegex } |
| 'password' | Password with min length | { type: String, minlength: 6 } |
| 'url' | URL with validation | { type: String, match: urlRegex } |
| 'phone' | Phone with validation | { type: String, match: phoneRegex } |
| 'color' | Hex color with validation | { type: String, match: hexColorRegex } |
| 'userRef' | Reference to User model | { type: ObjectId, ref: 'User' } |
| 'postRef' | Reference to Post model | { type: ObjectId, ref: 'Post' } |
| 'productRef'| Reference to Product model | { type: ObjectId, ref: 'Product' } |
| 'orderRef' | Reference to Order model | { type: ObjectId, ref: 'Order' } |
| 'categoryRef'| Reference to Category model | { type: ObjectId, ref: 'Category' } |
| 'point' | GeoJSON Point | { type: { type: String, enum: ['Point'] }, coordinates: [Number] } |
| 'location' | Full location object | Address, city, country with 2dsphere coordinates |
Advanced Schema with Validations
const productSchema = {
name: {
type: 'string',
required: true,
minlength: [3, 'Name must be at least 3 characters'],
maxlength: [50, 'Name cannot exceed 50 characters']
},
price: {
type: 'number',
required: true,
min: [0, 'Price cannot be negative']
},
category: {
type: 'string',
enum: ['Electronics', 'Clothing', 'Books'],
default: 'Electronics'
},
inStock: {
type: 'boolean',
default: true
}
};🎨 Models
Create models with automatic features and enhancements:
// Create a model
const User = mongoo.model('User', {
firstName: 'string!',
lastName: 'string!',
email: 'email!!',
birthDate: 'date?'
});
// Use predefined template
const User = mongoo.model('User', mongoo.templates.user);
// Create discriminator (inheritance)
const ClickEvent = mongoo.discriminator('Event', 'ClickEvent', {
elementId: 'string!',
coordinates: { x: 'number!', y: 'number!' }
});Auto-Features Included:
- ✅ Timestamps: Automatic
createdAtandupdatedAtfields - ✅ Indexes: Auto-indexing for common fields (status, category, userId, etc.)
- ✅ Virtuals: Automatic virtual fields (
id,fullName,age,createdAtFormatted, etc.) - ✅ Middleware: Auto-hooks for slugification, password hashing, soft deletes
- ✅ Validation: Smart validation with friendly messages
- ✅ Methods: Built-in instance methods (
updateFields,softDelete,toJSON) - ✅ Statics: Built-in static methods (
findActive,findDeleted,restore,findBySlug) - ✅ Query Helpers: Built-in query helpers (
byStatus,recent,popular)
🔨 CRUD Operations
Complete CRUD operations with simplified syntax:
Create
// Create a single document
const user = await mongoo.create('User', {
name: 'Alice',
email: '[email protected]',
age: 28
});
// Create multiple documents
const users = await mongoo.create('User', [
{ name: 'Bob', email: '[email protected]' },
{ name: 'Charlie', email: '[email protected]' }
]);Read
// Find all documents
const users = await mongoo.find('User');
// Find with filter
const activeUsers = await mongoo.find('User', {
isActive: true,
age: { $gte: 18 }
});
// Find one document
const user = await mongoo.findOne('User', { email: '[email protected]' });
// Find with options
const users = await mongoo.find('User', {}, {
sort: { createdAt: -1 },
limit: 10,
select: 'name email',
populate: 'posts',
lean: true
});
// Check if exists
const exists = await mongoo.exists('User', { email: '[email protected]' });
// Count documents
const count = await mongoo.count('User', { isActive: true });
// Get distinct values
const cities = await mongoo.distinct('User', 'city', { isActive: true });Update
// Update multiple documents
const result = await mongoo.update('User',
{ isActive: false },
{ status: 'inactive' }
);
console.log(`Modified ${result.modifiedCount} documents`);
// Find one and update
const updatedUser = await mongoo.findOneAndUpdate('User',
{ email: '[email protected]' },
{ age: 29 },
{ new: true } // Return updated document
);Delete
// Delete multiple documents
const result = await mongoo.delete('User', { isActive: false });
console.log(`Deleted ${result.deletedCount} documents`);
// Find one and delete
const deletedUser = await mongoo.findOneAndDelete('User',
{ email: '[email protected]' }
);🆔 ID-Based Operations
Easy-Mongoo provides comprehensive ID-based operations with automatic error handling:
Basic ID Operations
// Find by ID
const user = await mongoo.findById('User', '507f1f77bcf86cd799439011');
// Find by ID with options
const user = await mongoo.findById('User', userId, {
select: 'name email',
populate: 'posts',
lean: true
});
// Find by ID or throw error
const user = await mongoo.findByIdOrFail('User', userId);
// Throws error if not found
// Check if ID exists
const exists = await mongoo.existsById('User', userId);
console.log(exists); // true or falseUpdate by ID
// Find by ID and update (returns updated document)
const updatedUser = await mongoo.findByIdAndUpdate('User', userId, {
age: 31,
lastLogin: new Date()
}, {
new: true, // Return updated document (default: true)
runValidators: true // Run schema validators (default: true)
});
// Update by ID (returns operation result)
const result = await mongoo.updateById('User', userId, {
age: 31
});
console.log(`Modified: ${result.modifiedCount}`);
// Update by ID or throw error
const user = await mongoo.findByIdAndUpdateOrFail('User', userId, {
status: 'active'
});Delete by ID
// Find by ID and delete (returns deleted document)
const deletedUser = await mongoo.findByIdAndDelete('User', userId);
// Delete by ID (returns operation result)
const result = await mongoo.deleteById('User', userId);
console.log(`Deleted: ${result.deletedCount}`);
// Delete by ID or throw error
const user = await mongoo.findByIdAndDeleteOrFail('User', userId);Upsert by ID
// Find by ID and upsert (create if not exists)
const user = await mongoo.findByIdAndUpsert('User', userId, {
name: 'Alice',
email: '[email protected]',
age: 28
});
// Creates new document if ID doesn't exist
// Updates existing document if ID exists📦 Batch Operations
Perform operations on multiple documents by ID:
// Find multiple documents by IDs
const users = await mongoo.findByIds('User', [
'507f1f77bcf86cd799439011',
'507f1f77bcf86cd799439012',
'507f1f77bcf86cd799439013'
], {
select: 'name email',
sort: { createdAt: -1 }
});
// Update multiple documents by IDs
const result = await mongoo.updateByIds('User',
[userId1, userId2, userId3],
{ status: 'active', lastLogin: new Date() }
);
console.log(`Modified ${result.modifiedCount} documents`);
// Delete multiple documents by IDs
const result = await mongoo.deleteByIds('User',
[userId1, userId2, userId3]
);
console.log(`Deleted ${result.deletedCount} documents`);🔧 Field-Specific Updates
Perform atomic updates on specific fields:
Increment/Decrement
// Increment a field
const user = await mongoo.findByIdAndIncrement('User', userId, 'loginCount', 1);
// Decrement a field (use negative value)
const product = await mongoo.findByIdAndIncrement('Product', productId, 'stock', -5);
// Increment multiple fields
const stats = await mongoo.findByIdAndUpdate('User', userId, {
$inc: { loginCount: 1, points: 10 }
});Array Operations
// Push to array
const user = await mongoo.findByIdAndPush('User', userId, 'tags', 'premium');
// Push multiple values
const user = await mongoo.findByIdAndUpdate('User', userId, {
$push: { tags: { $each: ['premium', 'verified'] } }
});
// Pull from array
const user = await mongoo.findByIdAndPull('User', userId, 'tags', 'guest');
// Add to set (only if not exists)
const user = await mongoo.findByIdAndUpdate('User', userId, {
$addToSet: { tags: 'premium' }
});🔄 Status Operations
Built-in methods for common status changes:
// Activate user
const user = await mongoo.findByIdAndActivate('User', userId);
// Sets: { isActive: true, activatedAt: new Date() }
// Deactivate user
const user = await mongoo.findByIdAndDeactivate('User', userId);
// Sets: { isActive: false, deactivatedAt: new Date() }
// Archive document
const post = await mongoo.findByIdAndArchive('Post', postId);
// Sets: { status: 'archived', archivedAt: new Date() }
// Publish document
const post = await mongoo.findByIdAndPublish('Post', postId);
// Sets: { status: 'published', publishedAt: new Date() }🗑️ Soft Delete Operations
Implement soft deletes without physically removing data:
// Soft delete (mark as deleted)
const user = await mongoo.findByIdAndSoftDelete('User', userId, {
deletedBy: currentUserId // Optional: track who deleted
});
// Sets: { deleted: true, deletedAt: new Date(), deletedBy: userId }
// Restore soft-deleted document
const user = await mongoo.findByIdAndRestore('User', userId);
// Sets: { deleted: false, deletedAt: null, deletedBy: null }
// Find active (non-deleted) documents
const User = mongoo.getModel('User');
const activeUsers = await User.findActive();
// Find deleted documents
const deletedUsers = await User.findDeleted();
// Restore using static method
await User.restore(userId);🎭 Virtual Fields
Virtual fields are computed properties that don't get stored in MongoDB:
// Auto virtuals (already included)
// - id: String version of _id
// - fullName: firstName + lastName (if both exist)
// - age: Calculated from birthDate (if exists)
// - createdAtFormatted: Formatted createdAt date
// - updatedAtFormatted: Formatted updatedAt date
// Custom virtual field (getter only)
mongoo.virtual('User', 'profileUrl', function() {
return `/users/${this.slug || this._id}`;
});
// Virtual with getter and setter
mongoo.virtual('User', 'fullName',
// Getter
function() {
return `${this.firstName} ${this.lastName}`;
},
// Setter
function(value) {
const parts = value.split(' ');
this.firstName = parts[0];
this.lastName = parts.slice(1).join(' ');
}
);
// Usage
const user = await mongoo.findById('User', userId);
console.log(user.fullName); // "John Doe"
console.log(user.profileUrl); // "/users/507f1f77bcf86cd799439011"
console.log(user.age); // 30 (calculated from birthDate)
// Set via virtual
user.fullName = "Jane Smith";
console.log(user.firstName); // "Jane"
console.log(user.lastName); // "Smith"🔧 Methods & Statics
Add custom instance methods and static methods to your models:
Instance Methods
// Add instance method
mongoo.method('User', 'getProfile', function() {
return {
name: this.name,
email: this.email,
memberSince: this.createdAt,
profileUrl: `/users/${this._id}`
};
});
// Instance method with async operations
mongoo.method('User', 'deactivate', async function(reason) {
this.isActive = false;
this.deactivationReason = reason;
this.deactivatedAt = new Date();
return await this.save();
});
// Usage
const user = await mongoo.findById('User', userId);
const profile = user.getProfile();
await user.deactivate('User requested');Built-in Instance Methods:
toJSON()- Convert document to JSON with id fieldupdateFields(fields)- Update multiple fields and savesoftDelete()- Mark document as deleted
Static Methods
// Add static method
mongoo.static('User', 'findByEmail', async function(email) {
return await this.findOne({ email }).populate('posts');
});
// Static method with aggregation
mongoo.static('User', 'getActiveStats', async function() {
const stats = await this.aggregate([
{ $match: { isActive: true } },
{
$group: {
_id: null,
total: { $sum: 1 },
avgAge: { $avg: '$age' },
maxAge: { $max: '$age' },
minAge: { $min: '$age' }
}
}
]);
return stats[0] || {};
});
// Usage
const User = mongoo.getModel('User');
const user = await User.findByEmail('[email protected]');
const stats = await User.getActiveStats();Built-in Static Methods:
findActive()- Find all non-deleted documentsfindDeleted()- Find all deleted documentsrestore(id)- Restore a soft-deleted documentfindBySlug(slug)- Find document by slug
Query Helpers
// Add query helper
mongoo.query('User', 'byAgeRange', function(min, max) {
return this.where('age').gte(min).lte(max);
});
// Query helper for text search
mongoo.query('User', 'search', function(text) {
return this.find({
$or: [
{ name: { $regex: text, $options: 'i' } },
{ email: { $regex: text, $options: 'i' } }
]
});
});
// Usage
const adults = await mongoo.find('User').byAgeRange(18, 65);
const searchResults = await mongoo.find('User').search('alice');Built-in Query Helpers:
.byStatus(status)- Filter by status field.recent(days)- Filter by createdAt within last N days.popular(minViews)- Filter by viewCount greater than N
🪝 Middleware (Hooks)
Middleware (hooks) allow you to execute functions before or after certain operations:
Pre Hooks
// Pre-save hook
mongoo.pre('User', 'save', async function(next) {
if (this.isModified('password')) {
this.password = await bcrypt.hash(this.password, 12);
}
next();
});
// Pre-remove hook
mongoo.pre('User', 'remove', async function(next) {
// Remove user's posts when user is deleted
await mongoo.delete('Post', { author: this._id });
next();
});
// Pre-find hook
mongoo.pre('User', 'find', function(next) {
// Only find active users by default
this.where({ isActive: true });
next();
});
// Pre-findOneAndUpdate hook
mongoo.pre('User', 'findOneAndUpdate', function(next) {
this.set({ updatedAt: new Date() });
next();
});Post Hooks
// Post-save hook
mongoo.post('User', 'save', function(doc) {
console.log(`User ${doc.name} was saved`);
});
// Post-remove hook
mongoo.post('User', 'remove', function(doc) {
console.log(`User ${doc.name} was removed`);
});
// Post-find hook
mongoo.post('User', 'find', function(docs) {
console.log(`Found ${docs.length} users`);
});Aggregate Hooks
// Pre-aggregate hook (auto-applied to exclude deleted)
mongoo.pre('User', 'aggregate', function(next) {
this.pipeline().unshift({ $match: { deleted: { $ne: true } } });
next();
});Auto-Applied Middleware:
- Auto-slugify: Generates slug from name/title field
- Auto-hash passwords: Hashes password field on save
- Auto-update timestamps: Updates
updatedAton findOneAndUpdate - Logging: Logs save/remove operations
- Soft delete filter: Excludes deleted documents in aggregation
💳 Transactions
Execute multiple operations as a single atomic transaction:
Basic Transaction
// With automatic commit/rollback
await mongoo.withTransaction(async (session) => {
// Create user
const user = await mongoo.create('User', {
name: 'Alice',
email: '[email protected]'
}, { session });
// Create user's first post
await mongoo.create('Post', {
title: 'First Post',
content: 'Hello World!',
author: user._id
}, { session });
// If any operation fails, entire transaction is rolled back
});Transaction with Retry Logic
// Automatically retry transaction on transient errors
await mongoo.withRetryTransaction(async (session) => {
// Transfer money between accounts
await mongoo.update(
'Account',
{ _id: fromAccountId, balance: { $gte: 100 } },
{ $inc: { balance: -100 } },
{ session }
);
await mongoo.update(
'Account',
{ _id: toAccountId },
{ $inc: { balance: 100 } },
{ session }
);
}, 3); // Retry up to 3 timesManual Session Management
const session = await mongoo.startSession();
try {
session.startTransaction();
// Your operations here
const user = await mongoo.create('User', userData, { session });
await mongoo.create('Post', postData, { session });
await session.commitTransaction();
console.log('Transaction successful');
} catch (error) {
await session.abortTransaction();
console.error('Transaction failed:', error);
throw error;
} finally {
session.endSession();
}📊 Aggregation
Perform complex data analysis with MongoDB aggregation pipeline:
Basic Aggregation
// Group by category
const categoryStats = await mongoo.aggregate('Product', [
{
$group: {
_id: '$category',
totalProducts: { $sum: 1 },
avgPrice: { $avg: '$price' },
maxPrice: { $max: '$price' },
minPrice: { $min: '$price' }
}
},
{ $sort: { totalProducts: -1 } }
]);
// Lookup (join collections)
const userOrders = await mongoo.aggregate('Order', [
{
$lookup: {
from: 'users',
localField: 'customer',
foreignField: '_id',
as: 'customerInfo'
}
},
{ $unwind: '$customerInfo' },
{
$project: {
orderNumber: 1,
total: 1,
customerName: '$customerInfo.name',
customerEmail: '$customerInfo.email'
}
}
]);Advanced Pipeline
// Sales report with multiple stages
const salesReport = await mongoo.aggregate('Order', [
// Match recent completed orders
{
$match: {
createdAt: { $gte: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000) },
status: 'completed'
}
},
// Unwind items array
{ $unwind: '$items' },
// Lookup product details
{
$lookup: {
from: 'products',
localField: 'items.product',
foreignField: '_id',
as: 'productInfo'
}
},
{ $unwind: '$productInfo' },
// Group by product category
{
$group: {
_id: '$productInfo.category',
totalRevenue: { $sum: '$items.total' },
totalUnits: { $sum: '$items.quantity' },
avgOrderValue: { $avg: '$total' }
}
},
{ $sort: { totalRevenue: -1 } }
]);Faceted Search
// Multiple aggregations in one query
const facetedResults = await mongoo.aggregate('Product', [
{ $match: { price: { $lte: 1000 }, isActive: true } },
{
$facet: {
// Price ranges
priceRanges: [
{
$bucket: {
groupBy: '$price',
boundaries: [0, 50, 100, 200, 500, 1000],
default: 'Other',
output: {
count: { $sum: 1 },
products: { $push: '$name' }
}
}
}
],
// Categories
categories: [
{ $group: { _id: '$category', count: { $sum: 1 } } },
{ $sort: { count: -1 } },
{ $limit: 10 }
],
// Total count
totalCount: [{ $count: 'count' }]
}
}
]);🔍 Indexes
Create and manage indexes for better query performance:
// Create single field index
await mongoo.createIndex('User', { email: 1 });
// Create compound index
await mongoo.createIndex('User', { lastName: 1, firstName: 1 });
// Create unique index
await mongoo.createIndex('User', { email: 1 }, { unique: true });
// Create text index for full-text search
await mongoo.createIndex('Product', { name: 'text', description: 'text' });
// Create TTL index (auto-delete documents after time)
await mongoo.createIndex('Session', { createdAt: 1 }, { expireAfterSeconds: 3600 });
// Create geospatial index
await mongoo.createIndex('Location', { coordinates: '2dsphere' });
// Sync indexes (create and remove)
await mongoo.syncIndexes('User');Auto-Created Indexes:
- Text search: name, title, description fields
- Compound: status + createdAt, category + price, userId + createdAt
- TTL: expiresAt field (if exists)
- Geospatial: location.coordinates (if exists)
- Partial: isActive (if exists)
🔌 Plugins
Extend Easy-Mongoo with custom plugins:
// Create a plugin
const timestampPlugin = function(schema, options) {
schema.add({
createdAt: { type: Date, default: Date.now },
updatedAt: { type: Date, default: Date.now }
});
schema.pre('save', function(next) {
this.updatedAt = Date.now();
next();});
};
// Apply plugin to a specific model
mongoo.plugin('User', timestampPlugin);
// Apply plugin globally to all future models
mongoo.globalPlugin(timestampPlugin);
// Plugin with options
const softDeletePlugin = function(schema, options) {
schema.add({
deleted: { type: Boolean, default: false },
deletedAt: Date
});
schema.pre('find', function() {
if (!options.includeDeleted) {
this.where({ deleted: false });
}
});
schema.methods.softDelete = function() {
this.deleted = true;
this.deletedAt = new Date();
return this.save();
};
};
mongoo.plugin('User', softDeletePlugin, { includeDeleted: false });🌟 Advanced Features
Pagination
// Paginate results with full metadata
const result = await mongoo.paginate('User',
{ isActive: true }, // filter
{
page: 1,
limit: 10,
sort: { createdAt: -1 },
select: 'name email',
populate: 'posts'
}
);
console.log(result);
// {
// docs: [...], // Array of documents
// total: 100, // Total matching documents
// page: 1, // Current page
// limit: 10, // Items per page
// pages: 10, // Total pages
// hasNext: true, // Has next page
// hasPrev: false // Has previous page
// }Bulk Operations
// Bulk write operations
await mongoo.bulkWrite('User', [
{
insertOne: {
document: { name: 'Alice', email: '[email protected]' }
}
},
{
updateOne: {
filter: { email: '[email protected]' },
update: { $set: { status: 'active' } }
}
},
{
deleteOne: {
filter: { email: '[email protected]' }
}
}
]);Text Search
// Perform text search (requires text index)
const articles = await mongoo.textSearch('Article', 'mongodb database', {
limit: 10,
skip: 0
});
// Returns results sorted by text scoreGeospatial Queries
// Find nearby locations
const nearbyStores = await mongoo.near(
'Store',
'location',
[-73.97, 40.77], // [longitude, latitude]
5000, // 5km in meters
{ limit: 10 }
);Data Migration
// Migrate existing data
const processed = await mongoo.migrate('User', async (doc) => {
// Transform each document
if (!doc.slug) {
doc.slug = doc.name.toLowerCase().replace(/\s/g, '-');
await doc.save();
}
}, {
filter: { slug: { $exists: false } },
batchSize: 100
});
console.log(`Migrated ${processed} documents`);Seeding Data
// Seed database with initial data
await mongoo.seed('User', [
{ name: 'Admin', email: '[email protected]', role: 'admin' },
{ name: 'User1', email: '[email protected]', role: 'user' },
{ name: 'User2', email: '[email protected]', role: 'user' }
], {
clearFirst: true // Clear existing data first
});Export/Import Data
// Export data
const users = await mongoo.exportData('User', {
filter: { isActive: true },
fields: 'name email createdAt'
});
// Import data
await mongoo.importData('User', users);Query Explanation
// Explain query execution plan
const explanation = await mongoo.explain('User', 'find', { isActive: true });
console.log(explanation);
// Shows index usage, execution stats, etc.Caching
// Cache query results
const users = await mongoo.cache('User', 'active-users', async () => {
return await mongoo.find('User', { isActive: true });
}, 3600); // Cache for 1 hour
// Clear cache
mongoo.clearCache('User:active-users');
mongoo.clearCache(); // Clear all cache🎯 Predefined Templates
Easy-Mongoo includes ready-to-use schema templates for common use cases:
User Template
mongoo.model('User', mongoo.templates.user);Includes:
- firstName, lastName (required)
- email (required, unique, validated)
- password (hashed automatically)
- avatar, birthDate, phone, bio
- isActive, isVerified (with defaults)
- role (enum: user/admin/moderator)
- permissions (array)
- loginCount, lastLogin
- settings (theme, notifications, language)
- location (address, city, country, coordinates)
Product Template
mongoo.model('Product', mongoo.templates.product);Includes:
- name (required), sku (required, unique)
- description, category, tags
- price, comparePrice, cost (validated)
- inventory (quantity, tracking, allow out of stock)
- isActive, isFeatured, isDigital
- images, thumbnail
- seo (title, description, slug)
- viewCount, purchaseCount
Post Template
mongoo.model('Post', mongoo.templates.post);Includes:
- title, content (required), excerpt
- author, coAuthors (user references)
- status (draft/published/archived)
- visibility (public/private/members)
- categories, tags
- featuredImage, gallery
- viewCount, likeCount, commentCount
- meta (SEO fields)
Order Template
mongoo.model('Order', mongoo.templates.order);Includes:
- orderNumber (required, unique)
- status (pending/confirmed/processing/shipped/delivered/cancelled/refunded)
- customer (user reference), email
- shippingAddress (complete address object)
- items (array with product, price, quantity)
- subtotal, tax, shipping, discount, total
- paymentStatus, paymentMethod, transactionId
❌ Error Handling
Easy-Mongoo provides comprehensive error handling with user-friendly messages:
try {
await mongoo.create('User', {
email: 'invalid-email' // Will fail validation
});
} catch (error) {
console.log(error.message);
// "Validation failed: Please enter a valid email"
}Auto-Handled Error Types:
| Error Type | Description | User-Friendly Message | |------------|-------------|----------------------| | ValidationError | Schema validation failed | "Validation failed: [field] is required" | | CastError | Invalid ID format | "Invalid ID format" | | Duplicate Key (11000) | Unique constraint violation | "[field] already exists. Please use a different value." | | Document Not Found | ID not found | "[Model] with ID [id] not found" |
Error Options:
// Don't throw error if not found
const user = await mongoo.findById('User', userId, {
throwIfNotFound: false
});
// Returns null instead of throwing
// Using "OrFail" methods
const user = await mongoo.findByIdOrFail('User', userId);
// Always throws if not found🐛 Debugging
Enable debugging to see detailed operation logs:
// Enable debug mode on connect
await mongoo.connect('mongodb://localhost:27017/mydb', {
debug: true
});
// Enable/disable anytime
mongoo.setDebug(true); // Enable
mongoo.setDebug(false); // Disable
// Debug output examples:
// ✅ MongoDB Connected Successfully!
// 📊 Database: mydb
// 📝 Model 'User' created with auto-features
// 💾 User saved: 507f1f77bcf86cd799439011
// ✅ Created User
// 🔍 Found 10 User documents🧰 Utility Methods
Database Operations
// Drop entire database
await mongoo.dropDatabase();
// Clear all collections
await mongoo.clearAll();
// Get MongoDB types
const ObjectId = mongoo.ObjectId;
const Schema = mongoo.Schema;
const Types = mongoo.Types;
// Access underlying mongoose instance
const mongoose = mongoo.mongoose;🆚 Mongoose vs Easy-Mongoo
Connection Comparison
Mongoose:
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/mydb', {
useNewUrlParser: true,
useUnifiedTopology: true
})
.then(() => console.log('Connected'))
.catch(err => console.error('Error:', err));Easy-Mongoo:
const mongoo = require('easy-mongoo');
await mongoo.connect('mongodb://localhost:27017/mydb');Schema Definition Comparison
Mongoose:
const userSchema = new mongoose.Schema({
firstName: {
type: String,
required: [true, 'First name is required'],
trim: true
},
lastName: {
type: String,
required: [true, 'Last name is required'],
trim: true
},
email: {
type: String,
required: [true, 'Email is required'],
unique: true,
lowercase: true,
match: [/^\S+@\S+\.\S+$/, 'Please enter a valid email']
},
age: Number,
isActive: { type: Boolean, default: true }
}, { timestamps: true });
userSchema.virtual('fullName').get(function() {
return `${this.firstName} ${this.lastName}`;
});
const User = mongoose.model('User', userSchema);Easy-Mongoo:
mongoo.model('User', {
firstName: 'string!',
lastName: 'string!',
email: 'email!!',
age: 'number?',
isActive: 'boolean+'
});
// fullName virtual is auto-generated!CRUD Comparison
Mongoose (50+ lines):
// CREATE
const user = new User({
firstName: 'John',
lastName: 'Doe',
email: '[email protected]'
});
try {
await user.save();
} catch (error) {
console.error('Error:', error);
}
// READ
try {
const users = await User.find({ isActive: true })
.sort({ createdAt: -1 })
.limit(10);
} catch (error) {
console.error('Error:', error);
}
// UPDATE
try {
await User.findByIdAndUpdate(userId, { age: 31 }, { new: true });
} catch (error) {
console.error('Error:', error);
}
// DELETE
try {
await User.findByIdAndDelete(userId);
} catch (error) {
console.error('Error:', error);
}Easy-Mongoo (4 lines):
// CREATE, READ, UPDATE, DELETE with auto error handling
await mongoo.create('User', { firstName: 'John', lastName: 'Doe', email: '[email protected]' });
const users = await mongoo.find('User', { isActive: true }, { sort: { createdAt: -1 }, limit: 10 });
await mongoo.findByIdAndUpdate('User', userId, { age: 31 });
await mongoo.findByIdAndDelete('User', userId);Key Advantages
| Feature | Mongoose | Easy-Mongoo | Improvement | |---------|----------|-------------|-------------| | Lines of Code | 100+ for basic setup | 20 for same setup | 80% reduction | | Schema Shortcuts | ❌ No | ✅ Yes (30+ shortcuts) | Much faster | | Auto Virtuals | ❌ Manual | ✅ Auto (fullName, age, etc.) | Time saver | | Auto Indexes | ❌ Manual | ✅ Auto (text, compound, geo) | Time saver | | Error Handling | ❌ Manual try-catch | ✅ Automatic | Better UX | | Timestamps | ⚠️ Need to enable | ✅ Always included | Convenience | | Soft Deletes | ❌ Custom plugin | ✅ Built-in methods | Productivity | | Pagination | ❌ Manual calculation | ✅ One method call | Huge time saver | | Templates | ❌ No | ✅ 4 ready templates | Quick start | | Learning Curve | ⚠️ Steep | ✅ Gentle | Beginner friendly |
📚 Complete API Reference
Connection
connect(uri, options)- Connect to MongoDBdisconnect()- Disconnect from MongoDBstatus()- Get connection statusstartSession()- Start a new session
Models
model(name, schema, options)- Create modeldiscriminator(baseModel, name, schema)- Create discriminatorschema(definition, options)- Create schema
Basic CRUD
create(model, data, options)- Create document(s)find(model, filter, options)- Find documentsfindOne(model, filter, options)- Find one documentfindOneAndUpdate(model, filter, data, options)- Find and updatefindOneAndDelete(model, filter, options)- Find and deleteupdate(model, filter, data, options)- Update manydelete(model, filter, options)- Delete manycount(model, filter)- Count documentsexists(model, filter)- Check existencedistinct(model, field, filter)- Get distinct values
ID Operations
findById(model, id, options)- Find by IDfindByIdOrFail(model, id, options)- Find by ID or throwexistsById(model, id)- Check if ID existsfindByIdAndUpdate(model, id, data, options)- Update by IDfindByIdAndUpdateOrFail(model, id, data, options)- Update by ID or throwupdateById(model, id, data, options)- Update by ID (returns result)findByIdAndDelete(model, id, options)- Delete by IDfindByIdAndDeleteOrFail(model, id, options)- Delete by ID or throwdeleteById(model, id, options)- Delete by ID (returns result)findByIdAndUpsert(model, id, data, options)- Upsert by ID
Batch ID Operations
findByIds(model, ids, options)- Find multiple by IDsupdateByIds(model, ids, data, options)- Update multiple by IDsdeleteByIds(model, ids, options)- Delete multiple by IDs
Field Updates
findByIdAndIncrement(model, id, field, value, options)- Increment fieldfindByIdAndPush(model, id, field, value, options)- Push to arrayfindByIdAndPull(model, id, field, value, options)- Pull from array
Status Operations
findByIdAndActivate(model, id, options)- Activate documentfindByIdAndDeactivate(model, id, options)- Deactivate documentfindByIdAndArchive(model, id, options)- Archive documentfindByIdAndPublish(model, id, options)- Publish document
Soft Deletes
findByIdAndSoftDelete(model, id, options)- Soft deletefindByIdAndRestore(model, id, options)- Restore deleted
Advanced
aggregate(model, pipeline, options)- Run aggregationpaginate(model, filter, options)- Paginate resultstextSearch(model, text, options)- Text searchnear(model, field, coords, distance, options)- Geospatial querybulkWrite(model, operations, options)- Bulk operations
Transactions
withTransaction(callback)- Execute transactionwithRetryTransaction(callback, maxRetries)- Transaction with retry
Schema Extensions
virtual(model, name, getter, setter)- Add virtualmethod(model, name, fn)- Add instance methodstatic(model, name, fn)- Add static methodquery(model, name, fn)- Add query helperpre(model, hook, fn)- Add pre hookpost(model, hook, fn)- Add post hookplugin(model, plugin, options)- Add pluginglobalPlugin(plugin, options)- Add global plugin
Indexes
createIndex(model, fields, options)- Create indexsyncIndexes(model)- Sync indexes
Data Management
migrate(model, fn, options)- Migrate dataseed(model, data, options)- Seed dataexportData(model, options)- Export dataimportData(model, data, options)- Import data
Utilities
explain(model, operation, ...args)- Query explanationcache(model, key, fn, ttl)- Cache resultsclearCache(pattern)- Clear cachesetDebug(enabled)- Enable/disable debugdropDatabase()- Drop databaseclearAll()- Clear all collections
Properties
mongoose- Access mongoose instanceObjectId- MongoDB ObjectId typeSchema- Mongoose Schema classTypes- Mongoose Typestemplates- Predefined schema templates (user, product, post, order)
🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
🙏 Acknowledgments
- Built on top of the amazing Mongoose library
- Inspired by the need for simpler MongoDB operations
- Thanks to all contributors and users
📞 Support
- 📧 Email: [email protected]
- 🐛 Issues: GitHub Issues
- 📖 Documentation: https://easy-mongoo.zenuxs.in
- 💬 Discord: Join our community
🗺️ Roadmap
- [ ] TypeScript support with auto-generated types
- [ ] Redis caching integration
- [ ] GraphQL adapter
- [ ] Real-time change streams API
- [ ] Advanced query builder UI
- [ ] Performance monitoring dashboard
- [ ] Database migration CLI
- [ ] Visual schema designer
Made with ❤️ by developers, for developers
Star ⭐ this repo if you find it useful!
