Up until now, we have been interacting with our database exclusively through mongosh or Compass. But users of your website are not going to open a terminal to read your blog posts.
You need a backend server to act as a middleman. The server accepts HTTP requests from your website’s frontend (like React or standard HTML), talks to the database to get the data, and sends it back to the user.
In this module, we will learn how to connect a Node.js server to MongoDB using the official Native Driver.
1. Installing the MongoDB Driver
To allow Node.js to talk to MongoDB, we need to install the official mongodb NPM package (also known as the Native Driver).
Assuming you have an empty folder initialized with npm init -y, open your terminal and install the driver:
npm install mongodb
(Note: In modern Node.js applications, we use import syntax. Ensure you have "type": "module" set in your package.json!)
2. Connecting to MongoDB
Connecting to the database is an asynchronous operation. You must wait for the connection to establish before you try to read or write data.
Create a file named app.js and write the following connection boilerplate:
import { MongoClient } from 'mongodb';
// Replace with your actual connection string!
// If using Atlas, remember to include your username and password.
const uri = "mongodb://localhost:27017";
// Create a new MongoClient
const client = new MongoClient(uri);
async function run() {
try {
// Connect the client to the server
await client.connect();
console.log("Successfully connected to MongoDB!");
// Access a specific database and collection
const database = client.db('library');
const collection = database.collection('books');
// We will write our CRUD operations here!
} catch (error) {
console.error("Connection failed:", error);
} finally {
// Ensures that the client will close when you finish/error
await client.close();
}
}
// Execute the function
run();
3. CRUD Operations in Node.js
The commands in the Node.js driver are nearly identical to the commands we used in the MongoDB Shell, with one major difference: they return Promises. You must use await when calling them.
Let’s look at how to perform CRUD inside our try block.
Create (Insert)
const newBook = { title: "Dune", author: "Frank Herbert", pages: 412 };
// Insert the document
const result = await collection.insertOne(newBook);
console.log(`A document was inserted with the _id: ${result.insertedId}`);
Read (Find)
Retrieving data requires two steps. find() returns a “Cursor” (a pointer to the results), which you must then convert into a JavaScript array using toArray().
// Find all books by Frank Herbert
const cursor = collection.find({ author: "Frank Herbert" });
// Convert the cursor to an array of objects
const books = await cursor.toArray();
console.log(books);
Update
// Update the pages of Dune
const filter = { title: "Dune" };
const updateDoc = {
$set: { pages: 420, isAvailable: true }
};
const result = await collection.updateOne(filter, updateDoc);
console.log(`${result.modifiedCount} document(s) was updated.`);
Delete
// Delete the book
const result = await collection.deleteOne({ title: "Dune" });
console.log(`${result.deletedCount} document(s) was deleted.`);
4. Running Aggregations in Node.js
You can run complex aggregation pipelines exactly as you would in the shell. Just like find(), the aggregate() method returns a cursor that you must convert to an array.
const pipeline = [
{ $match: { isAvailable: true } },
{ $group: { _id: "$author", totalBooks: { $sum: 1 } } },
{ $sort: { totalBooks: -1 } }
];
const cursor = collection.aggregate(pipeline);
const results = await cursor.toArray();
console.log("Aggregation Results:", results);
5. Error Handling and Best Practices
When building a production server, managing your database connection properly is critical.
Do Not Connect on Every Request!
A massive mistake beginners make is calling client.connect() and client.close() inside every single API route. Establishing a network connection to a database is a slow, heavy operation.
Best Practice: Connect to the database once when your Node.js server starts up. Keep that connection open, and reuse it for all incoming web requests. Only close the connection when the Node.js server is completely shutting down.
Handling Network Errors
Your database might temporarily go offline due to a network glitch. Always wrap your database operations in try/catch blocks. If an operation fails, you must catch the error and send a polite 500 Internal Server Error response back to your website’s user, rather than letting your entire Node.js server crash!
Summary
Connecting MongoDB to Node.js is incredibly seamless because they both speak the same language: JavaScript and JSON.
- Install the
mongodbNPM package. - Use
MongoClient.connect()to establish a connection. - All database operations are asynchronous and return Promises.
- Connect once when the server starts and reuse that connection.
While the Native Driver is powerful, writing raw database queries can get messy as your application grows. In Module 21, we will introduce the most popular tool in the Node.js ecosystem for managing MongoDB: Mongoose!
Discussion
Loading comments...