Mongoose (ODM)
Mongoose: schema + model layer for MongoDB on Node. Validation, middleware, populate, virtuals.
MongoDB — Mongoose
EXAMPLE
// Install: npm install mongoose
import mongoose, { Schema } from 'mongoose';
// ===== Connect =====
await mongoose.connect(process.env.MONGO_URI!);
// ===== Schema =====
const userSchema = new Schema({
email: { type: String, required: true, unique: true, lowercase: true },
name: { type: String, required: true },
age: { type: Number, min: 0 },
tags: [String],
createdAt: { type: Date, default: Date.now },
}, { timestamps: true });
// ===== Model =====
const User = mongoose.model('User', userSchema);
// ===== CRUD =====
const u = await User.create({ email: 'a@x.io', name: 'Alex' });
const u2 = await User.findOne({ email: 'a@x.io' });
const u3 = await User.findById(id);
const list = await User.find({ age: { $gte: 18 } }).sort({ createdAt: -1 }).limit(20);
await User.updateOne({ _id: id }, { $set: { name: 'Sam' } });
await User.findByIdAndUpdate(id, { name: 'Sam' }, { new: true }); // returns updated doc
await User.deleteOne({ _id: id });
// ===== Validation =====
const u4 = new User({ email: 'invalid' });
try {
await u4.validate();
} catch (err) {
console.log(err.errors);
}
// ===== Middleware (hooks) =====
userSchema.pre('save', async function () {
if (this.isModified('password')) {
this.password = await hash(this.password);
}
});
userSchema.post('save', function (doc) {
console.log('saved', doc._id);
});
// ===== Virtuals (computed fields) =====
userSchema.virtual('displayName').get(function () {
return \`${this.name} <${this.email}>\`;
});
// ===== References + populate =====
const postSchema = new Schema({
title: String,
author: { type: Schema.Types.ObjectId, ref: 'User' },
});
const Post = mongoose.model('Post', postSchema);
const posts = await Post.find().populate('author', 'name email');
// ===== Discriminator (single-collection inheritance) =====
const baseSchema = new Schema({ type: String });
const Base = mongoose.model('Base', baseSchema);
const Admin = Base.discriminator('Admin', new Schema({ permissions: [String] }));
// ===== TypeScript =====
interface IUser {
email: string;
name: string;
createdAt?: Date;
}
const User2 = mongoose.model<IUser>('User2', userSchema);
// ===== Patterns =====
// - Schemas for validation + hooks
// - timestamps: true for createdAt/updatedAt automatic
// - populate() for joins
// - Lean queries (.lean()) for read-only perf (skip Mongoose docs)
// - Use the native driver when you need maximum perf
// ===== Pitfalls =====
// - .find() without .lean() in hot read paths -> Mongoose doc overhead
// - populate everywhere -> N+1 hidden
// - Schema-only validation (apps can write directly via driver)
// - Mixing Mongoose with the native driver inconsistently
Why it matters
Mongoose adds schemas, validation, hooks, virtuals, populate over the raw MongoDB driver. Use it when schema discipline + relations earn their ceremony; reach for the driver when you need raw perf or thin layers.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
import mongoose from 'mongoose';
const User = mongoose.model('User', new mongoose.Schema({ name: String, age: Number }));
await User.create({ name: 'Ada', age: 36 });
Try it Yourself »
Exercise
Mongoose model factory.
mongoose.
('User', schema)
Five letters.
Discussion
Loading…