iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

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)

Discussion

Loading…