Chapter 26: MongoDB Node.js Database Interaction
1. What exactly is “MongoDB + Node.js Database Interaction”?
In simple words:
It means using the official MongoDB Node.js Driver to let your JavaScript (Node.js) application connect, read, write, update, delete, run aggregations, listen to real-time changes, and manage transactions with a MongoDB database (usually Atlas).
Almost every serious Node.js backend (Express, NestJS, Fastify, Next.js API routes, serverless functions, etc.) uses this driver when it needs to talk to MongoDB.
No mongoose today — we’ll focus on the official driver first (because you should understand the foundation before using any ORM/ODM like Mongoose).
2. Why the Official Driver is Important (2026 reality)
| Reason | Explanation |
|---|---|
| Official & maintained by MongoDB | Always supports latest server features (change streams, transactions, etc.) |
| Best performance & lowest latency | Native wire protocol, connection pooling, compression, bulk ops |
| Full feature parity | Everything you can do in mongosh → you can do in Node.js driver |
| Lightweight | ~300 KB — no heavy abstractions (unlike Mongoose) |
| Production default | Most companies start with official driver → add Mongoose only if needed |
3. Step-by-step — Complete Realistic Example (Express + MongoDB Atlas)
Let’s build a tiny student management API — copy-paste ready.
Step 1: Project setup
|
0 1 2 3 4 5 6 7 8 9 |
mkdir student-api cd student-api npm init -y npm install express mongodb dotenv |
Create .env file (never commit this!):
|
0 1 2 3 4 5 6 7 |
MONGODB_URI=mongodb+srv://<username>:<password>@cluster0.abcde.mongodb.net/?retryWrites=true&w=majority PORT=4000 |
Step 2: Main file — index.js
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
// index.js require('dotenv').config(); const express = require('express'); const { MongoClient, ObjectId } = require('mongodb'); const app = express(); app.use(express.json()); const uri = process.env.MONGODB_URI; const client = new MongoClient(uri, { // Modern best practices 2026 maxPoolSize: 50, minPoolSize: 5, retryWrites: true, retryReads: true, connectTimeoutMS: 10000, socketTimeoutMS: 20000, serverSelectionTimeoutMS: 5000, compressors: ['zstd'], // heartbeatFrequencyMS: 10000 // optional tuning }); let db; // Connect once at startup (singleton pattern) async function connectToDb() { try { await client.connect(); console.log('✅ Connected to MongoDB Atlas'); db = client.db('school2026'); } catch (error) { console.error('Connection failed:', error); process.exit(1); } } // Routes // GET all students app.get('/api/students', async (req, res) => { try { const students = await db.collection('students') .find({}) .sort({ createdAt: -1 }) .limit(50) .toArray(); res.json(students); } catch (err) { res.status(500).json({ error: err.message }); } }); // GET one student by ID app.get('/api/students/:id', async (req, res) => { try { const student = await db.collection('students').findOne({ _id: new ObjectId(req.params.id) }); if (!student) return res.status(404).json({ error: 'Student not found' }); res.json(student); } catch (err) { res.status(500).json({ error: err.message }); } }); // POST – create new student app.post('/api/students', async (req, res) => { try { const { name, age, city, marks } = req.body; if (!name || !age || !city) { return res.status(400).json({ error: 'name, age, city are required' }); } const result = await db.collection('students').insertOne({ name, age: Number(age), city, marks: marks || {}, createdAt: new Date(), updatedAt: new Date() }); res.status(201).json({ message: 'Student created', id: result.insertedId }); } catch (err) { res.status(500).json({ error: err.message }); } }); // PUT – update student app.put('/api/students/:id', async (req, res) => { try { const updates = req.body; if (updates.age) updates.age = Number(updates.age); const result = await db.collection('students').updateOne( { _id: new ObjectId(req.params.id) }, { $set: { ...updates, updatedAt: new Date() } } ); if (result.matchedCount === 0) { return res.status(404).json({ error: 'Student not found' }); } res.json({ message: 'Student updated', modifiedCount: result.modifiedCount }); } catch (err) { res.status(500).json({ error: err.message }); } }); // DELETE student app.delete('/api/students/:id', async (req, res) => { try { const result = await db.collection('students').deleteOne({ _id: new ObjectId(req.params.id) }); if (result.deletedCount === 0) { return res.status(404).json({ error: 'Student not found' }); } res.json({ message: 'Student deleted' }); } catch (err) { res.status(500).json({ error: err.message }); } }); // Bonus: simple aggregation endpoint app.get('/api/stats', async (req, res) => { try { const stats = await db.collection('students').aggregate([ { $match: { age: { $gte: 15 } } }, { $group: { _id: "$city", studentCount: { $sum: 1 }, avgAge: { $avg: "$age" }, topMath: { $max: "$marks.math" } }}, { $sort: { studentCount: -1 } }, { $limit: 5 }, { $project: { city: "$_id", totalStudents: "$studentCount", averageAge: { $round: ["$avgAge", 1] }, highestMathScore: "$topMath", _id: 0 }} ]).toArray(); res.json(stats); } catch (err) { res.status(500).json({ error: err.message }); } }); // Start server const PORT = process.env.PORT || 4000; connectToDb().then(() => { app.listen(PORT, () => { console.log(`🚀 Server running on http://localhost:${PORT}`); }); }); |
4. Quick Summary Table – Key Driver Methods You’ll Use Daily
| Operation | Method Example | Notes / Tip |
|---|---|---|
| Connect | client.connect() | Do once at startup |
| Get collection | db.collection(‘students’) | — |
| Insert one | insertOne(doc) | Returns { insertedId } |
| Insert many | insertMany([doc1, doc2]) | Bulk — very fast |
| Find | find(filter).sort().limit().toArray() | Always use .toArray() or cursor methods |
| Find one | findOne(filter) | Returns single doc or null |
| Update one/many | updateOne(filter, { $set: {…} }) | Use operators: $set, $inc, $push, etc. |
| Delete | deleteOne() / deleteMany() | Be careful with empty filter! |
| Aggregate | aggregate(pipeline).toArray() | Full power — same syntax as mongosh |
| Transactions | client.startSession() → withTransaction() | For ACID multi-doc operations |
5. Mini Exercise – Try Right Now!
- Create .env with your Atlas connection string
- Run the code above
- Use Postman / curl / browser:
- POST http://localhost:4000/api/students with JSON body
- GET http://localhost:4000/api/students
- GET http://localhost:4000/api/stats
Understood beta? This is how real Node.js apps talk to MongoDB — clean, efficient, production-ready.
Next class options:
- Mongoose vs raw driver — when to use which?
- Change Streams — real-time updates in Node.js
- Transactions — multi-document ACID in Node.js
- Error handling & retry patterns (very important)
- Or build a full REST API with authentication?
Tell me what excites you most — class is yours! 🚀❤️
Any doubt in the code or concepts? Ask freely — we’ll debug together 😄
