Node.js: MongoDB with Node.js
Last updated: 2026-08-26
1. Why Choose MongoDB
Alice’s e-commerce API faces a tricky problem: product attributes vary greatly across different categories—laptops have CPU and memory fields, clothing has size and color fields, and food has expiration date fields. If a relational database were used, the table structure would have to be modified every time a new category was added. MongoDB’s document model naturally supports records with different structures; each product document can have completely different fields without requiring any schema migration.
- MongoDB is a document-based database that stores data in BSON format, which is similar to JSON but supports more data types.
- The document model naturally aligns with Node.js's object model, reducing the overhead of ORM conversions
- No fixed schema; fields can be dynamically added or removed as the business evolves
- Strong horizontal scalability, suitable for high-concurrency read and write scenarios
- A wide range of query operators and indexing mechanisms to meet complex search requirements
| SQL Concepts | MongoDB Concepts | Description |
|---|---|---|
| Database | Database | Database, consistent terminology |
| Table | Collection | Table → Collection |
| Row | Document | Row → Document |
| Column | Field | Column → Field |
| Primary Key | _id (ObjectId) |
Primary key auto-generated |
| JOIN | $lookup (Aggregation) |
Different types of join queries |
| Schema | No Enforced Schema | Optional Validation Rule |
| Index | Index | Similar indexing mechanisms |
2. Installation and Connection
(1) Install the MongoDB driver
Use the official mongodb npm package to connect to a MongoDB server.
▶ Example: Installing a Driver
npm install mongodb
(2) Establish a client connection
MongoClient is the entry point for connecting to MongoDB; it specifies the address and options via a connection string.
▶ Example: Basic Connection
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('myapp');
return db;
} catch (err) {
console.error('Connection failed:', err.message);
process.exit(1);
}
}
(3) Connection Pools and Configuration Options
MongoClient Built-in connection pool; the pool size and behavior are controlled via options.
▶ Example: Connection with Connection Pool Configuration
const client = new MongoClient(uri, {
maxPoolSize: 10,
minPoolSize: 2,
maxIdleTimeMS: 30000,
serverSelectionTimeoutMS: 5000,
connectTimeoutMS: 10000,
});
| Configuration Option | Default Value | Description |
|---|---|---|
maxPoolSize |
100 | Maximum number of connections in the connection pool |
minPoolSize |
0 | Minimum number of connections in the connection pool |
maxIdleTimeMS |
0 | Idle connection timeout (0 = no timeout) |
serverSelectionTimeoutMS |
30000 | Server selection timed out |
connectTimeoutMS |
30000 | Connection establishment timed out |
socketTimeoutMS |
0 | Socket timeout |
retryWrites |
true | Automatically retry write operations |
(4) Gracefully Closing Connections
When the application exits, it must close connections and release resources.
▶ Example: Graceful Shutdown
process.on('SIGINT', async () => {
await client.close();
console.log('MongoDB connection closed');
process.exit(0);
});
3. CRUD Operations
(1) Insert a document
Use insertOne to insert a single document, and insertMany to insert multiple documents.
▶ Example: Inserting Product Documentation
const products = db.collection('products');
const result = await products.insertOne({
name: 'Mechanical Keyboard',
price: 89.99,
category: 'electronics',
specs: { switches: 'Cherry MX Blue', layout: 'ANSI' },
createdAt: new Date(),
});
console.log('Inserted ID:', result.insertedId);
(2) Search the documentation
find Returns the cursor; findOne returns a single document.
▶ Example: Querying Products
const product = await products.findOne({ category: 'electronics' });
console.log(product);
const cursor = products.find({ price: { $gt: 50 } });
const expensive = await cursor.toArray();
console.log(`${expensive.length} products found`);
(3) Update the documentation
updateOne Updates the first match; updateMany updates all matches.
▶ Example: Updating Product Prices
const updateResult = await products.updateOne(
{ name: 'Mechanical Keyboard' },
{ $set: { price: 79.99, updatedAt: new Date() } },
);
console.log('Modified count:', updateResult.modifiedCount);
(4) Delete a document
deleteOne Deletes the first match; deleteMany deletes all matches.
▶ Example: Deleting a Product
const deleteResult = await products.deleteOne({
name: 'Mechanical Keyboard',
});
console.log('Deleted count:', deleteResult.deletedCount);
(5) CRUD Methods Quick Reference
| Operation | Method | Return Value | Description |
|---|---|---|---|
| Insert a single record | insertOne(doc) |
{insertedId} |
Return the automatically generated ID |
| Insert Multiple | insertMany([doc]) |
{insertedIds, insertedCount} |
Batch Insert |
| Query a single record | findOne(filter) |
Document or null | Return the first match |
| Query multiple rows | find(filter) |
Cursor | Requires toArray() or iteration |
| Update a single entry | updateOne(filter, update) |
{modifiedCount} |
Update only the first entry |
| Update multiple entries | updateMany(filter, update) |
{modifiedCount} |
Update all matches |
| Delete a single entry | deleteOne(filter) |
{deletedCount} |
Delete the first match |
| Delete Multiple | deleteMany(filter) |
{deletedCount} |
Delete All Matches |
| Replace in Document | replaceOne(filter, doc) |
{modifiedCount} |
Replace in Entire Document |
4. ObjectId and Query Operators
(1) The ObjectId Mechanism
The default _id for each document is of type ObjectId; the 12-byte encoding includes a timestamp, machine identifier, and counter.
▶ Example: Using ObjectId
const { ObjectId } = require('mongodb');
const id = new ObjectId();
console.log('ID string:', id.toHexString());
console.log('Timestamp:', id.getTimestamp());
const product = await products.findOne({
_id: new ObjectId('6850a1b2c3d4e5f6a7b8c9d0'),
});
(2) Comparison Operators
▶ Example: Comparison Query
const expensive = await products.find({ price: { $gt: 100 } }).toArray();
const cheap = await products.find({ price: { $lt: 20 } }).toArray();
const midRange = await products.find({ price: { $gte: 50, $lte: 100 } }).toArray();
(3) Logical and Set Operators
▶ Example: $in and $or queries
const selected = await products.find({
category: { $in: ['electronics', 'books'] },
}).toArray();
const mixed = await products.find({
$or: [
{ price: { $lt: 10 } },
{ category: 'electronics' },
],
}).toArray();
(4) Regular Expression Queries
▶ Example: Regular expression matching product names
const matched = await products.find({
name: { $regex: /^Mechanical/i },
}).toArray();
(5) Quick Reference for Query Operators
| Operator | Syntax | Description |
|---|---|---|
$eq |
{field: {$eq: val}} |
equals (and {field: val}) |
$gt |
{field: {$gt: val}} |
Greater than |
$gte |
{field: {$gte: val}} |
Greater than or equal to |
$lt |
{field: {$lt: val}} |
less than |
$lte |
{field: {$lte: val}} |
Less than or equal to |
$ne |
{field: {$ne: val}} |
Not equal to |
$in |
{field: {$in: [v1,v2]}} |
Within the array |
$nin |
{field: {$nin: [v1,v2]}} |
Not in the array |
$or |
{$or: [{...},{...}]} |
or condition |
$and |
{$and: [{...},{...}]} |
Conditions |
$not |
{field: {$not: {...}}} |
Unselect |
$regex |
{field: {$regex: 'pattern'}} |
Regular expression match |
$exists |
{field: {$exists: true}} |
Does the field exist? |
5. Projection and Sorting
(1) Projection Control Return Field
Projection specifies which fields to return or exclude, thereby reducing network traffic.
▶ Example: Projection Query
const names = await products.find(
{},
{ projection: { name: 1, price: 1, _id: 0 } },
).toArray();
const withoutSpecs = await products.find(
{},
{ projection: { specs: 0, createdAt: 0 } },
).toArray();
(2) Sorting and Pagination
sort sorts the results, while skip and limit implement pagination.
▶ Example: Sorting and Pagination
const page = 2;
const pageSize = 10;
const sorted = await products.find({})
.sort({ price: -1, name: 1 })
.skip((page - 1) * pageSize)
.limit(pageSize)
.toArray();
1indicates ascending order;-1indicates descending order- Sorting by multiple fields takes effect in the order in which they are declared
skipPerformance is poor when there are too many records; for large datasets, it is recommended to use range queries instead.
6. Index Basics
(1) Create an index
Indexes speed up queries but increase write overhead and storage space.
▶ Example: Creating an Index
await products.createIndex({ name: 1 });
await products.createIndex({ category: 1, price: -1 });
await products.createIndex({ name: 'text' });
const indexes = await products.indexes();
console.log(indexes);
(2) Unique Indexes and Composite Indexes
▶ Example: Unique Index
await products.createIndex({ sku: 1 }, { unique: true });
- MongoDB automatically creates a unique index for
_id - Composite indexes follow the leftmost prefix principle
- Text indexes support full-text search; only one per collection
7. MongoDB Connection and Operation Process
flowchart TD
A[App Launch] --> B[Create MongoClient]
B --> C[client.connect]
C -->|Success| D[Get db Examples]
C -->|Failure| E[Error Handling/Retry]
E --> C
D --> F[Get collection]
F --> G{CRUD Operation}
G -->|Write| H[insertOne / insertMany]
G -->|Read| I[find / findOne]
G -->|Update| J[updateOne / updateMany]
G -->|Delete| K[deleteOne / deleteMany]
H --> L[Back insertedId]
I --> M[Back to the Document/Cursor]
J --> N[Back modifiedCount]
K --> O[Back deletedCount]
L --> P{Continue?}
M --> P
N --> P
O --> P
P -->|Yes| G
P -->|No| Q[client.close]
Q --> R[Exit the app]
8. Comprehensive Example: Product Management Data Access Layer
const { MongoClient, ObjectId } = require('mongodb');
class ProductRepository {
constructor(uri, dbName) {
this.client = new MongoClient(uri, {
maxPoolSize: 10,
serverSelectionTimeoutMS: 5000,
});
this.dbName = dbName;
this.collection = null;
}
async connect() {
await this.client.connect();
const db = this.client.db(this.dbName);
this.collection = db.collection('products');
await this.collection.createIndex({ name: 1 });
await this.collection.createIndex({ category: 1, price: -1 });
console.log('ProductRepository connected');
}
async create(productData) {
const doc = {
...productData,
createdAt: new Date(),
updatedAt: new Date(),
};
const result = await this.collection.insertOne(doc);
return { ...doc, _id: result.insertedId };
}
async findById(id) {
return await this.collection.findOne({
_id: new ObjectId(id),
});
}
async findByCategory(category, page = 1, pageSize = 10) {
const skip = (page - 1) * pageSize;
const [items, total] = await Promise.all([
this.collection.find({ category })
.sort({ price: -1 })
.skip(skip)
.limit(pageSize)
.project({ name: 1, price: 1, category: 1 })
.toArray(),
this.collection.countDocuments({ category }),
]);
return { items, total, page, pageSize };
}
async update(id, updates) {
const result = await this.collection.updateOne(
{ _id: new ObjectId(id) },
{ $set: { ...updates, updatedAt: new Date() } },
);
return result.modifiedCount > 0;
}
async delete(id) {
const result = await this.collection.deleteOne({
_id: new ObjectId(id),
});
return result.deletedCount > 0;
}
async disconnect() {
await this.client.close();
console.log('ProductRepository disconnected');
}
}
async function main() {
const repo = new ProductRepository(
'mongodb://localhost:27017',
'ecommerce',
);
try {
await repo.connect();
const created = await repo.create({
name: 'Wireless Mouse',
price: 29.99,
category: 'electronics',
specs: { dpi: 16000, buttons: 6 },
});
console.log('Created:', created._id);
const found = await repo.findById(created._id);
console.log('Found:', found.name);
await repo.update(created._id, { price: 24.99 });
const page = await repo.findByCategory('electronics', 1, 10);
console.log('Page items:', page.items.length);
await repo.delete(created._id);
console.log('Deleted');
} finally {
await repo.disconnect();
}
}
main().catch(console.error);
❓ FAQ
id.getTimestamp() without needing any additional fields.serverSelectionTimeoutMS parameter to limit the wait time, log the error in the catch block, and implement graceful degradation; in a production environment, we recommend using a retry mechanism and health checks._id, they cannot be mixed—either use all 1s to include the specified fields, or use all 0s to exclude them; _id is returned by default and can be set to 0 individually to exclude it.9. Exercises
- Write a script to connect to a local MongoDB instance and insert 5 product documents of different categories, each containing at least 3 different fields.
- Implement a query by price range ($gte/$lte) and sort the results in descending order by price, returning only the "name" and "price" fields
- Use the
$inand$regexcombination to search for products where the category is in the specified list and the name contains a specific keyword - Create composite indexes for frequently queried fields, and use
explain()to compare the differences in execution plans before and after indexing. - Create a generic paginated query function that accepts the parameters filter, projection, sort, page, and pageSize
📖 Summary
- 1 Why Choose MongoDB: Core Concepts and Usage
- 2 Key Concepts and Usage of Installation and Connection
- 3 Core Concepts and Usage of CRUD Operations
- 4 Core Concepts and Usage of ObjectId and Query Operators
- 5 Core Concepts and Usage of Projections and Sorting
- 6 Core Concepts and Usage of Indexing Basics
- 7 Core Concepts and Usage of MongoDB Connections and Operations
- 8 Comprehensive Example: Core Concepts and Usage of the Product Management Data Access Layer
📝 Exercises
- Complete all the code examples in this lesson and make sure each one runs correctly.
- Modify the comprehensive example and add your own extensions
- Review the official documentation, identify 1–2 APIs not covered in this lesson, and write test code for them.
- Reflection: How would you apply what you’ve learned in this lesson to a real-world project?
- Try to combine what you’ve learned in this lesson with material from previous lessons to build a small project.