MongoDB in Node.js

Working with MongoDB in Node.js can be done either with the official native MongoDB driver (for maximum speed and low abstraction) or with Mongoose (for schema validation, middleware, and business logic modeling).

Official Native MongoDB Driver

The native driver (mongodb) offers direct, low-overhead access to MongoDB wire protocol commands.

npm install mongodb

Connecting with MongoClient

import { MongoClient } from 'mongodb';

const uri = process.env.MONGODB_URI || 'mongodb://localhost:27017';
const client = new MongoClient(uri);

async function run() {
  try {
    await client.connect();
    const db = client.db('mydb');
    const users = db.collection('users');

    // Insert
    const insertResult = await users.insertOne({
      name: 'Alice',
      email: '[email protected]',
      createdAt: new Date(),
    });
    console.log('Inserted ID:', insertResult.insertedId);

    // Query
    const user = await users.findOne({ name: 'Alice' });
    console.log('Found user:', user);

    // Query multiple with cursor
    const activeUsers = await users
      .find({ active: true })
      .sort({ createdAt: -1 })
      .limit(10)
      .toArray();

    // Update
    await users.updateOne(
      { _id: insertResult.insertedId },
      { $set: { active: true } }
    );

    // Delete
    await users.deleteOne({ name: 'Alice' });
  } finally {
    await client.close();
  }
}

run().catch(console.dir);

Mongoose (Object Data Modeling - ODM)

Mongoose provides schema validation, type casting, middleware hooks, and model relationships.

npm install mongoose

Connection Setup

import mongoose from 'mongoose';

const uri = process.env.MONGODB_URI || 'mongodb://localhost:27017/mydb';

await mongoose.connect(uri, {
  autoIndex: true, // Build indexes automatically in dev (disable in heavy production)
});

Defining Schemas and Models

import mongoose, { Schema } from 'mongoose';

const userSchema = new Schema(
  {
    name: {
      type: String,
      required: [true, 'Name is required'],
      trim: true,
    },
    email: {
      type: String,
      required: true,
      unique: true,
      lowercase: true,
    },
    role: {
      type: String,
      enum: ['user', 'admin'],
      default: 'user',
    },
    age: {
      type: Number,
      min: 0,
      max: 150,
    },
  },
  {
    timestamps: true, // Automatically manages createdAt and updatedAt
  }
);

// Indexes
userSchema.index({ email: 1 });

// Instance method
userSchema.methods.sayHello = function () {
  return `Hello, I'm ${this.name}!`;
};

// Compile into Model
export const User = mongoose.model('User', userSchema);

CRUD Operations with Mongoose

import { User } from './models/User.js';

// 1. Create
const user = await User.create({
  name: 'Bob',
  email: '[email protected]',
  age: 28,
});

// 2. Read
const found = await User.findOne({ email: '[email protected]' });
const allUsers = await User.find({ age: { $gte: 18 } }).select('-__v').lean();

// 3. Update
const updated = await User.findOneAndUpdate(
  { email: '[email protected]' },
  { $set: { age: 29 } },
  { new: true, runValidators: true } // Return updated doc, run validations
);

// 4. Delete
await User.deleteOne({ email: '[email protected]' });

Native Driver vs. Mongoose

Feature Native Driver (mongodb) Mongoose
Abstraction Level Low (Direct BSON/Wire protocol) High (ODM with schemas & models)
Performance Highest throughput, low memory Slight overhead due to model wrapping
Validation Manual or via MongoDB JSON Schema Built-in declarative schema validators
Middleware / Hooks Not supported Pre/Post lifecycle hooks (pre('save'))
Population / Joins $lookup aggregation pipeline .populate() virtual references
Recommended Reading
Sponsored
JavaScript: The Definitive Guide
The JS Bible
JavaScript: The Definitive Guide
By David FlanaganView on Amazon