npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

easy-mongoo

v2.0.4

Published

Ultra-simple MongoDB wrapper with all mongoose features in easy syntax

Readme

Easy-Mongoo 🚀

npm version License: MIT Downloads

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

🔌 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 createdAt and updatedAt fields
  • ✅ 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 false

Update 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 field
  • updateFields(fields) - Update multiple fields and save
  • softDelete() - 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 documents
  • findDeleted() - Find all deleted documents
  • restore(id) - Restore a soft-deleted document
  • findBySlug(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 updatedAt on 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 times

Manual 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 score

Geospatial 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 MongoDB
  • disconnect() - Disconnect from MongoDB
  • status() - Get connection status
  • startSession() - Start a new session

Models

  • model(name, schema, options) - Create model
  • discriminator(baseModel, name, schema) - Create discriminator
  • schema(definition, options) - Create schema

Basic CRUD

  • create(model, data, options) - Create document(s)
  • find(model, filter, options) - Find documents
  • findOne(model, filter, options) - Find one document
  • findOneAndUpdate(model, filter, data, options) - Find and update
  • findOneAndDelete(model, filter, options) - Find and delete
  • update(model, filter, data, options) - Update many
  • delete(model, filter, options) - Delete many
  • count(model, filter) - Count documents
  • exists(model, filter) - Check existence
  • distinct(model, field, filter) - Get distinct values

ID Operations

  • findById(model, id, options) - Find by ID
  • findByIdOrFail(model, id, options) - Find by ID or throw
  • existsById(model, id) - Check if ID exists
  • findByIdAndUpdate(model, id, data, options) - Update by ID
  • findByIdAndUpdateOrFail(model, id, data, options) - Update by ID or throw
  • updateById(model, id, data, options) - Update by ID (returns result)
  • findByIdAndDelete(model, id, options) - Delete by ID
  • findByIdAndDeleteOrFail(model, id, options) - Delete by ID or throw
  • deleteById(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 IDs
  • updateByIds(model, ids, data, options) - Update multiple by IDs
  • deleteByIds(model, ids, options) - Delete multiple by IDs

Field Updates

  • findByIdAndIncrement(model, id, field, value, options) - Increment field
  • findByIdAndPush(model, id, field, value, options) - Push to array
  • findByIdAndPull(model, id, field, value, options) - Pull from array

Status Operations

  • findByIdAndActivate(model, id, options) - Activate document
  • findByIdAndDeactivate(model, id, options) - Deactivate document
  • findByIdAndArchive(model, id, options) - Archive document
  • findByIdAndPublish(model, id, options) - Publish document

Soft Deletes

  • findByIdAndSoftDelete(model, id, options) - Soft delete
  • findByIdAndRestore(model, id, options) - Restore deleted

Advanced

  • aggregate(model, pipeline, options) - Run aggregation
  • paginate(model, filter, options) - Paginate results
  • textSearch(model, text, options) - Text search
  • near(model, field, coords, distance, options) - Geospatial query
  • bulkWrite(model, operations, options) - Bulk operations

Transactions

  • withTransaction(callback) - Execute transaction
  • withRetryTransaction(callback, maxRetries) - Transaction with retry

Schema Extensions

  • virtual(model, name, getter, setter) - Add virtual
  • method(model, name, fn) - Add instance method
  • static(model, name, fn) - Add static method
  • query(model, name, fn) - Add query helper
  • pre(model, hook, fn) - Add pre hook
  • post(model, hook, fn) - Add post hook
  • plugin(model, plugin, options) - Add plugin
  • globalPlugin(plugin, options) - Add global plugin

Indexes

  • createIndex(model, fields, options) - Create index
  • syncIndexes(model) - Sync indexes

Data Management

  • migrate(model, fn, options) - Migrate data
  • seed(model, data, options) - Seed data
  • exportData(model, options) - Export data
  • importData(model, data, options) - Import data

Utilities

  • explain(model, operation, ...args) - Query explanation
  • cache(model, key, fn, ttl) - Cache results
  • clearCache(pattern) - Clear cache
  • setDebug(enabled) - Enable/disable debug
  • dropDatabase() - Drop database
  • clearAll() - Clear all collections

Properties

  • mongoose - Access mongoose instance
  • ObjectId - MongoDB ObjectId type
  • Schema - Mongoose Schema class
  • Types - Mongoose Types
  • templates - Predefined schema templates (user, product, post, order)

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/AmazingFeature)
  3. Commit your changes (git commit -m 'Add some AmazingFeature')
  4. Push to the branch (git push origin feature/AmazingFeature)
  5. 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

🗺️ 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!