Table of Contents
- Introduction
- What is CRUD?
- Setting Up the Project
- Connecting to MongoDB
- Create Operation (
insertOne
,insertMany
) - Read Operation (
findOne
,find
) - Update Operation (
updateOne
,updateMany
) - Delete Operation (
deleteOne
,deleteMany
) - Error Handling & Validation
- Organizing Code Structure
- Best Practices
- 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
mkdir mongodb-crud-node
cd mongodb-crud-node
npm init -y
npm install mongodb dotenv
Setup .env
File
MONGO_URI=mongodb://127.0.0.1:27017
DB_NAME=crud_demo
Folder Structure
.
├── .env
├── db.js
├── crud.js
└── index.js
4. Connecting to MongoDB
db.js
const { 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
const db = await connectDB();
const users = db.collection('users');
await users.insertOne({
name: 'John Doe',
email: '[email protected]',
age: 30
});
insertMany
await users.insertMany([
{ name: 'Jane Doe', age: 25 },
{ name: 'Alice', age: 22 }
]);
6. Read Operation
findOne
const user = await users.findOne({ name: 'John Doe' });
console.log(user);
find + toArray
const userList = await users.find({ age: { $gt: 20 } }).toArray();
console.log(userList);
Add filters, projection, and sorting:
const usersSorted = await users.find({}, { projection: { name: 1, _id: 0 } }).sort({ age: -1 }).toArray();
7. Update Operation
updateOne
await users.updateOne(
{ name: 'John Doe' },
{ $set: { age: 31 } }
);
updateMany
await users.updateMany(
{ age: { $lt: 30 } },
{ $inc: { age: 1 } }
);
8. Delete Operation
deleteOne
await users.deleteOne({ name: 'John Doe' });
deleteMany
await users.deleteMany({ age: { $gt: 25 } });
9. Error Handling & Validation
Wrap each database interaction in try-catch
to gracefully handle errors:
try {
const user = await users.insertOne({ name: 'Test' });
} catch (err) {
console.error('MongoDB Error:', err.message);
}
Use validation logic before making DB calls:
if (!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:
/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.