CRUD Operations in Node.js with MongoDB (with Code Examples)

Table of Contents

  1. Introduction
  2. What is CRUD?
  3. Setting Up the Project
  4. Connecting to MongoDB
  5. Create Operation (insertOne, insertMany)
  6. Read Operation (findOne, find)
  7. Update Operation (updateOne, updateMany)
  8. Delete Operation (deleteOne, deleteMany)
  9. Error Handling & Validation
  10. Organizing Code Structure
  11. Best Practices
  12. Conclusion

1. Introduction

MongoDB and Node.js are often paired together in modern web development due to their asynchronous capabilities and JSON-friendly nature. In this module, we’ll explore how to implement CRUD operations (Create, Read, Update, Delete) using the official MongoDB Node.js driver, not an ODM like Mongoose, giving you full control and transparency.


2. What is CRUD?

CRUD is an acronym for the four basic types of database operations:

  • Create – Add new data
  • Read – Retrieve data
  • Update – Modify existing data
  • Delete – Remove data

Each of these maps directly to methods provided by the MongoDB Node.js driver.


3. Setting Up the Project

Initialize Project

bashCopyEditmkdir mongodb-crud-node
cd mongodb-crud-node
npm init -y
npm install mongodb dotenv

Setup .env File

envCopyEditMONGO_URI=mongodb://127.0.0.1:27017
DB_NAME=crud_demo

Folder Structure

bashCopyEdit.
├── .env
├── db.js
├── crud.js
└── index.js

4. Connecting to MongoDB

db.js

jsCopyEditconst { MongoClient } = require('mongodb');
require('dotenv').config();

const client = new MongoClient(process.env.MONGO_URI);
let db;

async function connectDB() {
  if (!db) {
    await client.connect();
    db = client.db(process.env.DB_NAME);
    console.log('Connected to MongoDB');
  }
  return db;
}

module.exports = connectDB;

5. Create Operation

insertOne

jsCopyEditconst db = await connectDB();
const users = db.collection('users');

await users.insertOne({
  name: 'John Doe',
  email: '[email protected]',
  age: 30
});

insertMany

jsCopyEditawait users.insertMany([
  { name: 'Jane Doe', age: 25 },
  { name: 'Alice', age: 22 }
]);

6. Read Operation

findOne

jsCopyEditconst user = await users.findOne({ name: 'John Doe' });
console.log(user);

find + toArray

jsCopyEditconst userList = await users.find({ age: { $gt: 20 } }).toArray();
console.log(userList);

Add filters, projection, and sorting:

jsCopyEditconst usersSorted = await users.find({}, { projection: { name: 1, _id: 0 } }).sort({ age: -1 }).toArray();

7. Update Operation

updateOne

jsCopyEditawait users.updateOne(
  { name: 'John Doe' },
  { $set: { age: 31 } }
);

updateMany

jsCopyEditawait users.updateMany(
  { age: { $lt: 30 } },
  { $inc: { age: 1 } }
);

8. Delete Operation

deleteOne

jsCopyEditawait users.deleteOne({ name: 'John Doe' });

deleteMany

jsCopyEditawait users.deleteMany({ age: { $gt: 25 } });

9. Error Handling & Validation

Wrap each database interaction in try-catch to gracefully handle errors:

jsCopyEdittry {
  const user = await users.insertOne({ name: 'Test' });
} catch (err) {
  console.error('MongoDB Error:', err.message);
}

Use validation logic before making DB calls:

jsCopyEditif (!email.includes('@')) throw new Error('Invalid email address');

You can also enforce schema validation using MongoDB 4.0+ features on the collection level.


10. Organizing Code Structure

To make your CRUD application production-ready:

Structure like:

bashCopyEdit/db
  └── connect.js
/controllers
  └── userController.js
/routes
  └── userRoutes.js
index.js

Move each CRUD function to its own module in /controllers for clean separation.


11. Best Practices

  • Always close the connection in shutdown hooks.
  • Avoid hardcoded values – use environment variables.
  • Sanitize and validate user input.
  • Use appropriate indexes for frequent queries.
  • Prefer insertMany, updateMany, etc., for batch operations.
  • Keep the logic reusable by abstracting functions into services.
  • Use proper logging (e.g., Winston, Pino) in production.
  • Set a default limit on find() queries to avoid memory overload.

12. Conclusion

Implementing CRUD with the official MongoDB Node.js driver gives you low-level control over your database interactions. While tools like Mongoose abstract a lot, using the native driver teaches you how MongoDB works under the hood — a crucial skill for optimization, performance tuning, and working in microservices environments.