Easy-Mongoo
Ultra-simple MongoDB wrapper with all mongoose features in easy syntax
Introduction
Easy-Mongoo is a powerful yet simple wrapper for MongoDB with Mongoose. It provides an intuitive API that makes database operations effortless while maintaining all the advanced features of Mongoose.
Why Easy-Mongoo? Traditional Mongoose can be complex for beginners. Easy-Mongoo simplifies everything while keeping all the power!
One-Line Operations
Perform complex database operations with just one line of 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!
Installation
Install Easy-Mongoo using npm:
npm install easy-mongoo
Then require it in your project:
const mongoo = require('easy-mongoo');
Quick Start
Get started with Easy-Mongoo in just 3 steps:
// 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: 'john@example.com',
age: 30
});
console.log(user);
That's it! You've just created a MongoDB connection, defined a schema, and saved a document with just a few lines of code.
Connection
Connect to MongoDB with automatic error handling and logging:
// Basic connection
await mongoo.connect('mongodb://localhost:27017/mydb');
// With options and debug mode
await mongoo.connect('mongodb://localhost:27017/mydb', {
debug: true,
useNewUrlParser: true,
useUnifiedTopology: true
});
// Check connection status
const status = mongoo.status();
console.log(status);
// Disconnect when done
await mongoo.disconnect();
Connection Status Object
| Property | Description |
|---|---|
| connected | Boolean indicating connection status |
| database | Name of the connected database |
| host | Database host address |
| models | Array of registered model names |
| readyState | Mongoose connection state |
Schema Creation
Easy-Mongoo provides an ultra-simple schema system with smart shortcuts:
// Simple schema with 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!'
}
};
// 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
}
};
| Shortcut | Description | Equivalent To |
|---|---|---|
'string!' |
Required string | { type: String, required: true } |
'number?' |
Optional number | Number |
'boolean+' |
Boolean with default false | { type: Boolean, default: false } |
'email' |
Email with validation | { type: String, required: true, match: emailRegex } |
'password' |
Password with min length | { type: String, required: true, minlength: 6 } |
'url' |
URL with validation | { type: String, match: urlRegex } |
'userRef' |
Reference to User model | { type: ObjectId, ref: 'User' } |
Models
Create models with automatic features and enhancements:
// Create a model
const User = mongoo.model('User', {
firstName: 'string!',
lastName: 'string!',
email: 'email',
birthDate: 'date?'
});
// Get existing model
const User = mongoo.getModel('User');
// Use predefined template
const User = mongoo.model('User', mongoo.templates.user);
Auto-Features Included:
- Timestamps: Automatic createdAt and updatedAt fields
- Indexes: Auto-indexing for common fields
- Virtuals: Automatic virtual fields (fullName, age, etc.)
- Middleware: Auto-hooks for common operations
- Validation: Smart validation with friendly messages
CRUD Operations
Complete CRUD operations with simplified syntax:
// Create a single document
const user = await mongoo.create('User', {
name: 'Alice',
email: 'alice@example.com',
age: 28
});
// Create multiple documents
const users = await User.create([
{ name: 'Bob', email: 'bob@example.com' },
{ name: 'Charlie', email: 'charlie@example.com' }
]);
// Find or create
const user = await mongoo.findOrCreate('User',
{ email: 'alice@example.com' },
{ name: 'Alice', age: 28 }
);
// 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: 'alice@example.com'
});
// Find by ID
const user = await mongoo.findById('User', '507f1f77bcf86cd799439011');
// Find with options
const users = await mongoo.find('User', {}, {
sort: { createdAt: -1 },
limit: 10,
select: 'name email',
populate: 'posts'
});
// Update multiple documents
await mongoo.update('User',
{ isActive: false },
{ status: 'inactive' }
);
// Update by ID
const updatedUser = await mongoo.updateById(
'User',
'507f1f77bcf86cd799439011',
{ age: 29, lastLogin: new Date() }
);
// Save (create or update)
const user = await mongoo.save('User', {
_id: '507f1f77bcf86cd799439011', // If provided, updates existing
name: 'Alice Updated',
email: 'alice.updated@example.com'
});
// Delete multiple documents
await mongoo.delete('User', {
isActive: false
});
// Delete by ID
await mongoo.deleteById('User', '507f1f77bcf86cd799439011');
// Check if document exists
const exists = await mongoo.exists('User', {
email: 'alice@example.com'
});
// Count documents
const count = await mongoo.count('User', {
isActive: true
});
Virtual Fields NEW
Virtual fields are computed properties that don't get stored in MongoDB:
// Auto virtuals (already included)
// - fullName (if firstName and lastName exist)
// - age (if birthDate exists)
// - createdAtFormatted
// Custom virtual field
mongoo.virtual('User', 'profileUrl', function() {
return `/users/${this.slug || this._id}`;
});
// Virtual with 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', '...');
console.log(user.fullName); // Computed property
console.log(user.profileUrl); // Custom virtual
Methods & Statics
Add custom instance methods and static methods to your models:
// 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', '...');
const profile = user.getProfile();
await user.deactivate('User requested');
// Add static method
mongoo.static('User', 'findByEmail', async function(email) {
return await this.findOne({ email }).populate('posts');
});
// Static method with complex logic
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 = await mongoo.getModel('User').findByEmail('alice@example.com');
const stats = await mongoo.getModel('User').getActiveStats();
// 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');
Middleware (Hooks)
Middleware (hooks) allow you to execute functions before or after certain operations:
// 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();
});
// 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`);
});
// Pre-aggregate hook
mongoo.pre('User', 'aggregate', function(next) {
// Add match stage to exclude deleted users
this.pipeline().unshift({
$match: { isActive: true }
});
next();
});
// Post-aggregate hook
mongoo.post('User', 'aggregate', function(docs) {
console.log(`Aggregation returned ${docs.length} documents`);
});
Transactions
Execute multiple operations as a single atomic transaction:
// Basic transaction
await mongoo.withTransaction(async (session) => {
// Create user
const user = await mongoo.create('User', {
name: 'Alice',
email: 'alice@example.com'
}, { session });
// Create user's first post
await mongoo.create('Post', {
title: 'First Post',
content: 'Hello World!',
author: user._id
}, { session });
});
// Transaction with retry logic
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 times
// Manual session management
const session = await mongoo.startSession();
try {
session.startTransaction();
// Your operations here
await mongoo.create('User', userData, { session });
await session.commitTransaction();
} catch (error) {
await session.abortTransaction();
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 aggregation (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 aggregation with multiple stages
const salesReport = await mongoo.aggregate('Order', [
// Match orders from last 30 days
{
$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 product info
{
$unwind: '$productInfo'
},
// Group by product category
{
$group: {
_id: '$productInfo.category',
totalRevenue: { $sum: '$items.total' },
totalUnits: { $sum: '$items.quantity' },
avgOrderValue: { $avg: '$total' }
}
},
// Sort by revenue
{
$sort: { totalRevenue: -1 }
}
]);
// Faceted search with multiple aggregations
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' }
]
}
}
]);