Mongoose — Schema, Model & Methods (Complete Guide)

Definitions, categories, and practical examples of Model methods and related Mongoose features. Focus: MongoDB via Mongoose (Node.js).

Schema — Definition & Examples

A Schema defines the structure, types, validators, defaults and indexes for documents in a MongoDB collection.

Basic schema example

// user.schema.js
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
  name: { type: String, required: true },
  email: { type: String, required: true, unique: true, lowercase: true },
  age: { type: Number, min: 0 },
  createdAt: { type: Date, default: Date.now }
});
module.exports = userSchema;

Schema features

Model — Definition & Creation

A Model is created from a Schema and provides the API to interact with a MongoDB collection (CRUD, queries, aggregation, transactions, etc.).

Create a model

const mongoose = require('mongoose');
const userSchema = require('./user.schema');
const User = mongoose.model('User', userSchema); // creates 'users' collection (pluralized)

What models provide

Create — Insert documents

Method Purpose
Model.create(docs) Create & save one or many documents
new Model(doc).save() Instantiate document & save (runs setters, validation)
Model.insertMany(docs) Bulk insert many documents (fast)

Examples

// create single
const user = await User.create({ name:'A', email:'a@x.com' });

// insert many
await User.insertMany([{name:'B', email:'b@x.com'}, {name:'C', email:'c@x.com'}]);

// new + save
const u = new User({name:'D'}); await u.save();

Read — Querying documents

Method Purpose
Model.find(filter) Return array of docs
Model.findOne(filter) Return first matching doc
Model.findById(id) Find document by _id
Model.exists(filter) Boolean if any doc matches
Model.countDocuments(filter) Accurate count of matching docs
Model.distinct(field, filter) Unique field values

Examples

// find all users older than 18
const adults = await User.find({ age: { $gte: 18 } }).sort({name:1}).limit(50);

// find one
const u = await User.findOne({ email: 'a@x.com' });

// find by id
const single = await User.findById('64f...');

Update — Modify documents

Method Purpose / notes
Model.updateOne(filter, update) Update first match (no doc returned)
Model.updateMany(filter, update) Update multiple docs
Model.findOneAndUpdate(filter, update, opts) Find & update (can return new doc)
Model.findByIdAndUpdate(id, update, opts) Update by id (shorthand)
Document.save() Modify document fields then save (runs validation)

Examples

// updateOne
await User.updateOne({ email:'a@x.com' }, { $set: { age: 30 } });

// findOneAndUpdate - return updated doc
const updated = await User.findOneAndUpdate(
  { email:'a@x.com' },
  { $inc: { visits: 1 } },
  { new: true } // returns the NEW doc
);

// instance save
const doc = await User.findById(id); doc.name = 'New'; await doc.save();

Delete — Remove documents

Method Notes
Model.deleteOne(filter) Delete first match
Model.deleteMany(filter) Delete multiple docs
Model.findOneAndDelete(filter) Find & delete, returns deleted doc
Model.findByIdAndDelete(id) Delete by id

Examples

// delete single
await User.deleteOne({ email: 'x@x.com' });

// delete many (careful)
await User.deleteMany({ inactive: true });

// find and delete
const removed = await User.findByIdAndDelete(id);

Advanced Operations

Aggregation

Use MongoDB aggregation pipelines via Model.aggregate().
// group by month, count users
const pipeline = [
  { $match: { createdAt: { $gte: new Date('2024-01-01') } } },
  { $project: { month: { $month: '$createdAt' } } },
  { $group: { _id: '$month', count: { $sum: 1 } } }
];
const stats = await User.aggregate(pipeline);

Bulk Writes

High-performance bulk ops with Model.bulkWrite().
await User.bulkWrite([
  { insertOne: { document: { name:'A', email:'a@x.com' } } },
  { updateOne: { filter: { email:'b@x' }, update: { $set:{active:true} } } },
  { deleteOne: { filter: { old: true } } }
]);

Change Streams / watch()

Real-time changes via Model.watch() (requires replica set).
const changeStream = User.watch();
changeStream.on('change', (change) => console.log('changed', change));

Transactions

Use sessions for multi-document ACID transactions.
const session = await mongoose.startSession();
await session.withTransaction(async () => {
  await User.updateOne({ _id: id1 }, { $inc: { balance: -50 } }, { session });
  await Account.updateOne({ _id: id2 }, { $inc: { balance: +50 } }, { session });
});
session.endSession();

Miscellaneous & Utility Methods

Indexes

// define in schema
userSchema.index({ email: 1 });

// sync indexes
await User.syncIndexes();

Schema methods & statics

// instance method
userSchema.methods.sayHi = function(){ console.log('Hi', this.name); };

// static method
userSchema.statics.findByEmail = function(email){ return this.findOne({ email }); };

const User = mongoose.model('User', userSchema);
const u = await User.findByEmail('a@x.com');

Middleware (hooks)

pre/post for operations like save, validate, remove, updateOne.
userSchema.pre('save', function(next){
  if(this.isModified('password')) this.password = hash(this.password);
  next();
});

userSchema.post('remove', function(doc){ console.log('removed', doc._id); });

Hydrate

Convert plain JS object to Mongoose document.
const obj = { _id: '64f...', name:'X' };
const doc = User.hydrate(obj); // doc is a Mongoose document (no DB call)

createCollection()

Force collection creation / options.
await User.createCollection({ capped: true, size: 1024 });

Notes, Best Practices & Gotchas

Common patterns