Mongoose — MongoDB ODM for Node.js
You are an expert in Mongoose, the elegant MongoDB object modeling library for Node.js. You help developers define schemas with validation, build queries with a fluent API, use middleware hooks, populate references, create virtual fields, and handle transactions — providing structure and type safety on top of MongoDB's flexible document model.
Core Capabilities
Schema Definition
import mongoose, { Schema, Document, Model } from "mongoose";
interface IUser extends Document {
email: string;
name: string;
passwordHash: string;
role: "user" | "admin" | "moderator";
profile: { bio?: string; avatar?: string; website?: string };
posts: mongoose.Types.ObjectId[];
createdAt: Date;
updatedAt: Date;
fullName: string;
}
const userSchema = new Schema<IUser>(
{
email: {
type: String,
required: [true, "Email is required"],
unique: true,
lowercase: true,
trim: true,
match: [/^\S+@\S+\.\S+$/, "Invalid email format"],
},
name: { type: String, required: true, minlength: 2, maxlength: 100 },
passwordHash: { type: String, required: true, select: false },
role: { type: String, enum: ["user", "admin", "moderator"], default: "user" },
profile: {
bio: { type: String, maxlength: 500 },
avatar: String,
website: { type: String, match: /^https?:\/\// },
},
posts: [{ type: Schema.Types.ObjectId, ref: "Post" }],
},
{
timestamps: true,
toJSON: { virtuals: true },
toObject: { virtuals: true },
},
);
// Virtual field
userSchema.virtual("fullName").get(function () {
return this.name;
});
// Index for search
userSchema.index({ email: 1 });
userSchema.index({ name: "text", "profile.bio": "text" });
// Pre-save middleware
userSchema.pre("save", async function (next) {
if (this.isModified("passwordHash")) {
this.passwordHash = await bcrypt.hash(this.passwordHash, 12);
}
next();
});
// Static method
userSchema.statics.findByEmail = function (email: string) {
return this.findOne({ email: email.toLowerCase() });
};
// Instance method
userSchema.methods.verifyPassword = async function (password: string) {
return bcrypt.compare(password, this.passwordHash);
};
const User: Model<IUser> = mongoose.model("User", userSchema);
Queries
// Find with filters, sorting, pagination
const users = await User.find({ role: "user" })
.sort({ createdAt: -1 })
.skip(20).limit(10)
.select("name email profile.avatar")
.lean(); // Plain objects (faster)
// Populate references
const userWithPosts = await User.findById(id)
.populate({ path: "posts", select: "title createdAt", options: { limit: 5, sort: { createdAt: -1 } } });
// Aggregation pipeline
const stats = await User.aggregate([
{ $match: { createdAt: { $gte: thirtyDaysAgo } } },
{ $group: { _id: "$role", count: { $sum: 1 }, latest: { $max: "$createdAt" } } },
{ $sort: { count: -1 } },
]);
// Transactions
const session = await mongoose.startSession();
await session.withTransaction(async () => {
const user = await User.create([{ name: "Alice", email: "alice@example.com", passwordHash: "..." }], { session });
await Post.create([{ title: "First Post", author: user[0]._id }], { session });
});
Installation
npm install mongoose
Best Practices
- Schema validation — Define strict schemas; use
required, enum, match, min/max validators
- Lean queries — Use
.lean() for read-only queries; returns plain objects, 5x faster than Mongoose documents
- Indexes — Add indexes for fields you query/sort by; use
explain() to verify query plans
- Population — Use
populate() sparingly; for complex joins, prefer aggregation $lookup
- Middleware — Use
pre('save') for hashing, validation; post('save') for notifications, logging
- Timestamps — Enable
timestamps: true; auto-manages createdAt and updatedAt
- Transactions — Use sessions for multi-document operations; requires replica set or MongoDB Atlas
- TypeScript — Define interfaces extending
Document; use generics with Schema<IUser> for full type safety
1---2name: mongoose3description: You are an expert in Mongoose, the elegant MongoDB object modeling library for Node.js. You help developers define schemas with validation, build queries with a fluent API, use middleware hooks, populate references, create virtual fields, and handle transactions — providing structure and type safety on top of MongoDB's flexible document model.4license: Apache-2.05---67# Mongoose — MongoDB ODM for Node.js89You are an expert in Mongoose, the elegant MongoDB object modeling library for Node.js. You help developers define schemas with validation, build queries with a fluent API, use middleware hooks, populate references, create virtual fields, and handle transactions — providing structure and type safety on top of MongoDB's flexible document model.1011## Core Capabilities1213### Schema Definition1415```typescript16import mongoose, { Schema, Document, Model } from "mongoose";1718interface IUser extends Document {19 email: string;20 name: string;21 passwordHash: string;22 role: "user" | "admin" | "moderator";23 profile: { bio?: string; avatar?: string; website?: string };24 posts: mongoose.Types.ObjectId[];25 createdAt: Date;26 updatedAt: Date;27 fullName: string;28}2930const userSchema = new Schema<IUser>(31 {32 email: {33 type: String,34 required: [true, "Email is required"],35 unique: true,36 lowercase: true,37 trim: true,38 match: [/^\S+@\S+\.\S+$/, "Invalid email format"],39 },40 name: { type: String, required: true, minlength: 2, maxlength: 100 },41 passwordHash: { type: String, required: true, select: false },42 role: { type: String, enum: ["user", "admin", "moderator"], default: "user" },43 profile: {44 bio: { type: String, maxlength: 500 },45 avatar: String,46 website: { type: String, match: /^https?:\/\// },47 },48 posts: [{ type: Schema.Types.ObjectId, ref: "Post" }],49 },50 {51 timestamps: true,52 toJSON: { virtuals: true },53 toObject: { virtuals: true },54 },55);5657// Virtual field58userSchema.virtual("fullName").get(function () {59 return this.name;60});6162// Index for search63userSchema.index({ email: 1 });64userSchema.index({ name: "text", "profile.bio": "text" });6566// Pre-save middleware67userSchema.pre("save", async function (next) {68 if (this.isModified("passwordHash")) {69 this.passwordHash = await bcrypt.hash(this.passwordHash, 12);70 }71 next();72});7374// Static method75userSchema.statics.findByEmail = function (email: string) {76 return this.findOne({ email: email.toLowerCase() });77};7879// Instance method80userSchema.methods.verifyPassword = async function (password: string) {81 return bcrypt.compare(password, this.passwordHash);82};8384const User: Model<IUser> = mongoose.model("User", userSchema);85```8687### Queries8889```typescript90// Find with filters, sorting, pagination91const users = await User.find({ role: "user" })92 .sort({ createdAt: -1 })93 .skip(20).limit(10)94 .select("name email profile.avatar")95 .lean(); // Plain objects (faster)9697// Populate references98const userWithPosts = await User.findById(id)99 .populate({ path: "posts", select: "title createdAt", options: { limit: 5, sort: { createdAt: -1 } } });100101// Aggregation pipeline102const stats = await User.aggregate([103 { $match: { createdAt: { $gte: thirtyDaysAgo } } },104 { $group: { _id: "$role", count: { $sum: 1 }, latest: { $max: "$createdAt" } } },105 { $sort: { count: -1 } },106]);107108// Transactions109const session = await mongoose.startSession();110await session.withTransaction(async () => {111 const user = await User.create([{ name: "Alice", email: "alice@example.com", passwordHash: "..." }], { session });112 await Post.create([{ title: "First Post", author: user[0]._id }], { session });113});114```115116## Installation117118```bash119npm install mongoose120```121122## Best Practices1231241. **Schema validation** — Define strict schemas; use `required`, `enum`, `match`, `min/max` validators1252. **Lean queries** — Use `.lean()` for read-only queries; returns plain objects, 5x faster than Mongoose documents1263. **Indexes** — Add indexes for fields you query/sort by; use `explain()` to verify query plans1274. **Population** — Use `populate()` sparingly; for complex joins, prefer aggregation `$lookup`1285. **Middleware** — Use `pre('save')` for hashing, validation; `post('save')` for notifications, logging1296. **Timestamps** — Enable `timestamps: true`; auto-manages `createdAt` and `updatedAt`1307. **Transactions** — Use sessions for multi-document operations; requires replica set or MongoDB Atlas1318. **TypeScript** — Define interfaces extending `Document`; use generics with `Schema<IUser>` for full type safety