MongoDB with Node.js
Real applications talk to MongoDB through code rather than through the shell. Node.js is a popular choice for this job because it uses JavaScript, the same language that mongosh understands. MongoDB provides an official driver for Node.js, which is a library that sends commands to the database and returns the answers.
How the Pieces Connect
+-------------+ +------------------+ +----------------+
| Your Node | ---> | MongoDB driver | ---> | MongoDB server |
| application | | (npm package) | | (database) |
| app.js | <--- | | <--- | |
+-------------+ +------------------+ +----------------+
asks for data translates requests stores and finds dataThe application never speaks to the database directly. The driver translates JavaScript calls into the protocol that the server understands.
Set Up the Project
- Install Node.js from the official website and confirm it with
node --version. - Create a project folder and open a terminal inside it.
- Run
npm init -yto create a package.json file. - Run
npm install mongodbto add the driver.
Connect to the Database
Create a file named app.js and add the code below:
const { MongoClient } = require("mongodb");
const uri = "mongodb://localhost:27017";
const client = new MongoClient(uri);
async function main() {
try {
await client.connect();
console.log("Connected to MongoDB");
const db = client.db("shop");
const products = db.collection("products");
// database work goes here
} finally {
await client.close();
}
}
main().catch(console.error);Code Walkthrough
MongoClientis the class that manages the connection.uriholds the connection string with the server address.client.db("shop")selects the database.db.collection("products")selects the collection.- The
asyncandawaitkeywords make the code wait for each database reply. - The
finallyblock closes the connection even when an error occurs.
Run the file with node app.js.
CRUD Operations in Node.js
The method names match the shell commands, which makes the move from mongosh to code smooth.
Create
const result = await products.insertOne({ name: "Pen", price: 5 });
console.log("Inserted id:", result.insertedId);
await products.insertMany([
{ name: "Pencil", price: 3 },
{ name: "Ruler", price: 12 }
]);Read
const one = await products.findOne({ name: "Pen" });
console.log(one);
const cheap = await products.find({ price: { $lt: 10 } }).toArray();
console.log(cheap);The find method returns a cursor. The toArray() call converts the cursor into a normal JavaScript array.
Update
await products.updateOne({ name: "Pen" }, { $set: { price: 6 } });
await products.updateMany({ price: { $lt: 5 } }, { $inc: { price: 1 } });Delete
await products.deleteOne({ name: "Ruler" });
await products.deleteMany({ price: { $gt: 100 } });Sort, Limit, and Project in Code
Cursor methods chain together the same way they do in the shell. The query below finds the three most expensive products and returns only their names and prices:
const top3 = await products
.find({})
.sort({ price: -1 })
.limit(3)
.project({ _id: 0, name: 1, price: 1 })
.toArray();Each method adds one instruction to the query. MongoDB runs the complete query on the server and sends back only the final three results.
Loop Through a Large Result
A collection with millions of documents should not load into memory at once. Use a for await loop to read one document at a time:
const cursor = products.find({ price: { $gt: 0 } });
for await (const doc of cursor) {
console.log(doc.name);
}Work with ObjectId
Every document receives an _id of type ObjectId. A search by id needs the ObjectId class, because a plain text string does not match:
const { ObjectId } = require("mongodb");
const doc = await products.findOne({ _id: new ObjectId("64b7f0c2a1b2c3d4e5f60718") });Handle Errors
try {
await products.insertOne({ _id: 1, name: "Pen" });
await products.insertOne({ _id: 1, name: "Pencil" });
} catch (error) {
if (error.code === 11000) {
console.log("Duplicate key: that _id already exists");
} else {
console.error(error);
}
}The code 11000 signals a duplicate key. Reading the error code lets the application respond with a friendly message.
Keep Secrets Safe
Connection strings for real servers contain usernames and passwords. Never type them into the source code. Store them in an environment variable:
const uri = process.env.MONGODB_URI;Set the variable in the operating system or in a .env file that stays out of version control and public repositories.
One Client for the Whole Application
Create the MongoClient once when the application starts and reuse it for every request. The driver manages a pool of connections behind the scenes. Opening a new client for each request wastes time and can exhaust the server connections.
Mongoose: An Optional Layer
Mongoose is a popular library built on top of the driver. It lets developers describe a schema in code and adds validation and helper methods:
const mongoose = require("mongoose");
const productSchema = new mongoose.Schema({
name: { type: String, required: true },
price: { type: Number, min: 0 }
});
const Product = mongoose.model("Product", productSchema);
await Product.create({ name: "Pen", price: 5 });Beginners learn the plain driver first, and many teams add Mongoose later for larger projects.
Summary
The official MongoDB driver connects Node.js applications to the database. A MongoClient opens the connection, and methods such as insertOne, find, updateOne, and deleteOne mirror the shell commands. The async and await keywords keep the code readable. Environment variables protect passwords, a single shared client serves the whole application, and Mongoose offers schemas for projects that need them.
