Episode 15 of 17

MongoDB with Node.js

Integrate MongoDB with Node.js using both the official MongoDB driver and Mongoose ODM. Learn how to connect, perform CRUD operations, build schemas, use middleware, handle errors, and structure a production-ready Express API backed by MongoDB.

Node.js and MongoDB are a natural pair — both work with JavaScript and JSON-like data. There are two main ways to use MongoDB with Node.js: the official MongoDB driver (low-level, direct control) and Mongoose (ODM with schemas, validation, and middleware). This episode covers both.

Setup

# Initialize a new Node.js project
mkdir mongo-node-app
cd mongo-node-app
npm init -y

# Install the MongoDB driver
npm install mongodb

# Install Mongoose (ODM)
npm install mongoose

# Install Express for the API
npm install express

Using the Official MongoDB Driver

Connecting

const { MongoClient } = require('mongodb');

const uri = 'mongodb://localhost:27017';
const client = new MongoClient(uri);

async function connect() {
    try {
        await client.connect();
        console.log('Connected to MongoDB');
        const db = client.db('blogApp');
        return db;
    } catch (err) {
        console.error('Connection failed:', err.message);
        process.exit(1);
    }
}

// Always close the connection when done
process.on('SIGINT', async () => {
    await client.close();
    process.exit(0);
});

CRUD with Native Driver

async function main() {
    const db = await connect();
    const users = db.collection('users');

    // CREATE
    const result = await users.insertOne({
        name: 'Alice',
        email: 'alice@example.com',
        age: 28,
        createdAt: new Date()
    });
    console.log('Inserted:', result.insertedId);

    // READ
    const user = await users.findOne({ email: 'alice@example.com' });
    console.log('Found:', user);

    const allDevs = await users.find({ role: 'developer' }).toArray();
    console.log('Developers:', allDevs);

    // UPDATE
    await users.updateOne(
        { email: 'alice@example.com' },
        { $set: { age: 29 }, $inc: { loginCount: 1 } }
    );

    // DELETE
    await users.deleteOne({ email: 'test@example.com' });

    // COUNT
    const count = await users.countDocuments({ role: 'developer' });
    console.log('Developer count:', count);
}

main();

Using Mongoose (Recommended for Most Projects)

Connecting with Mongoose

const mongoose = require('mongoose');

mongoose.connect('mongodb://localhost:27017/blogApp', {
    // Mongoose 7+ doesn't need these options
})
.then(() => console.log('Mongoose connected'))
.catch(err => console.error('Connection error:', err));

Defining Schemas and Models

const { Schema, model } = require('mongoose');

// Define a schema
const userSchema = new Schema({
    name: {
        type: String,
        required: [true, 'Name is required'],
        trim: true,
        minlength: 2,
        maxlength: 100
    },
    email: {
        type: String,
        required: true,
        unique: true,
        lowercase: true,
        match: [/^\S+@\S+\.\S+$/, 'Invalid email format']
    },
    age: {
        type: Number,
        min: 0,
        max: 150
    },
    role: {
        type: String,
        enum: ['user', 'admin', 'moderator'],
        default: 'user'
    },
    tags: [String],
    address: {
        street: String,
        city: String,
        state: String,
        zip: String
    },
    isActive: {
        type: Boolean,
        default: true
    }
}, {
    timestamps: true  // Adds createdAt and updatedAt automatically
});

// Create the model
const User = model('User', userSchema);

Mongoose CRUD Operations

// CREATE
const user = await User.create({
    name: 'Alice',
    email: 'alice@example.com',
    age: 28,
    role: 'user'
});

// READ
const alice = await User.findOne({ email: 'alice@example.com' });
const users = await User.find({ role: 'user' }).sort({ name: 1 }).limit(10);
const userById = await User.findById('65a1b2c3d4e5f6a7b8c9d0e1');

// UPDATE
await User.findByIdAndUpdate(userId, {
    $set: { name: 'Alice Updated' }
}, { new: true, runValidators: true });

// DELETE
await User.findByIdAndDelete(userId);
await User.deleteMany({ isActive: false });

Mongoose Middleware (Hooks)

// Pre-save middleware — runs before saving
userSchema.pre('save', function(next) {
    // Hash password before saving
    if (this.isModified('password')) {
        this.password = hashPassword(this.password);
    }
    next();
});

// Post-save middleware — runs after saving
userSchema.post('save', function(doc) {
    console.log('User saved:', doc.email);
});

// Pre-find middleware
userSchema.pre('find', function() {
    // Only return active users by default
    this.where({ isActive: true });
});

Express API with MongoDB

const express = require('express');
const mongoose = require('mongoose');
const app = express();

app.use(express.json());

// Connect to MongoDB
mongoose.connect('mongodb://localhost:27017/blogApp');

// User model (from above)
const User = require('./models/User');

// GET all users
app.get('/api/users', async (req, res) => {
    try {
        const page = parseInt(req.query.page) || 1;
        const limit = parseInt(req.query.limit) || 10;
        const skip = (page - 1) * limit;

        const users = await User.find()
            .select('-password')
            .sort({ createdAt: -1 })
            .skip(skip)
            .limit(limit);

        const total = await User.countDocuments();

        res.json({
            data: users,
            pagination: { page, limit, total, pages: Math.ceil(total / limit) }
        });
    } catch (err) {
        res.status(500).json({ error: err.message });
    }
});

// POST create user
app.post('/api/users', async (req, res) => {
    try {
        const user = await User.create(req.body);
        res.status(201).json(user);
    } catch (err) {
        if (err.code === 11000) {
            return res.status(409).json({ error: 'Email already exists' });
        }
        res.status(400).json({ error: err.message });
    }
});

// GET single user
app.get('/api/users/:id', async (req, res) => {
    try {
        const user = await User.findById(req.params.id);
        if (!user) return res.status(404).json({ error: 'User not found' });
        res.json(user);
    } catch (err) {
        res.status(500).json({ error: err.message });
    }
});

// PUT update user
app.put('/api/users/:id', async (req, res) => {
    try {
        const user = await User.findByIdAndUpdate(
            req.params.id,
            req.body,
            { new: true, runValidators: true }
        );
        if (!user) return res.status(404).json({ error: 'User not found' });
        res.json(user);
    } catch (err) {
        res.status(400).json({ error: err.message });
    }
});

// DELETE user
app.delete('/api/users/:id', async (req, res) => {
    try {
        const user = await User.findByIdAndDelete(req.params.id);
        if (!user) return res.status(404).json({ error: 'User not found' });
        res.json({ message: 'User deleted' });
    } catch (err) {
        res.status(500).json({ error: err.message });
    }
});

app.listen(3000, () => console.log('Server running on port 3000'));

What's Next

You now have a complete Node.js API backed by MongoDB. In the next episode, we'll integrate MongoDB with PHP — connecting, performing CRUD, and using the MongoDB PHP library for server-side applications.

TutorialMongoDBNode.jsJavaScript