0Pricing
MongoDB Academy · Lesson

Connecting With the Official Node.js Driver

Learners will create a MongoClient, manage the connection pool, and run CRUD operations from a Node.js application using the native driver.

Connecting With the Official Node.js Driver is a free MongoDB Academy lesson on CoddyKit — lesson 1 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the MongoDB Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

The Official MongoDB Node.js Driver

The MongoDB Node.js driver (mongodb npm package) is the official, low-level library for connecting to MongoDB from Node.js applications. It provides direct access to all MongoDB features without an abstraction layer, making it ideal for performance-critical code, microservices, and scripts. The driver is maintained by MongoDB, Inc. and closely tracks new MongoDB server features.

// Install the driver
// npm install mongodb

// Or with yarn:
// yarn add mongodb

// The driver exports MongoClient as its main entry point
const { MongoClient, ObjectId, ServerApiVersion } = require('mongodb');
// or ES module:
// import { MongoClient, ObjectId } from 'mongodb';

Creating a MongoClient

The MongoClient class is the entry point for all interactions. Instantiate it with your connection string (URI) and an optional options object. The connection string encodes the host, port, credentials, and connection parameters. For MongoDB Atlas, copy the connection string from the Atlas UI and replace the placeholder password. Create one MongoClient per application and reuse it—do not create a new one per request.

const { MongoClient, ServerApiVersion } = require('mongodb');

const uri = process.env.MONGODB_URI;
// URI format: mongodb+srv://<user>:<password>@cluster0.xxxxx.mongodb.net/?retryWrites=true

const client = new MongoClient(uri, {
  serverApi: {
    version: ServerApiVersion.v1,
    strict: true,
    deprecationErrors: true
  }
});

// client is not yet connected — connecting happens lazily or via connect()

Connecting to MongoDB

Call client.connect() to establish a connection pool. The driver maintains a connection pool—typically 5-100 connections to the server—and reuses them across operations. You only need to call connect() once at application startup. Operations can also be called directly without explicit connect()—the driver connects lazily on the first operation. Call client.close() on application shutdown.

async function main() {
  try {
    await client.connect();
    console.log('Connected to MongoDB');

    // Get database reference
    const db = client.db('myDatabase');
    
    // Ping the server to verify connection
    await db.command({ ping: 1 });
    console.log('Ping successful');

    // Run your application logic here...
  } finally {
    // Always close when done
    await client.close();
  }
}

main().catch(console.error);

Singleton Pattern for Web Servers

In a web server (Express, Fastify, NestJS), the MongoClient must be a module-level singleton that is created once on startup and shared by all request handlers. Creating a new MongoClient per request would exhaust available connections and severely degrade performance. The standard pattern is to connect in the server startup function and export the client or db reference.

// db.js — module-level singleton
const { MongoClient } = require('mongodb');

let client;
let db;

async function connectToDatabase() {
  if (db) return db; // return existing connection
  client = new MongoClient(process.env.MONGODB_URI);
  await client.connect();
  db = client.db(process.env.DB_NAME || 'myapp');
  return db;
}

module.exports = { connectToDatabase };

// In your Express app:
// const { connectToDatabase } = require('./db');
// const db = await connectToDatabase();
// app.locals.db = db;

Getting Collection References

Access a collection by calling db.collection('collectionName'). This returns a Collection object without any network call—it is just a reference. You can get collection references at startup and store them as module-level variables, or get them inline within each function. Collection references are lightweight and safe to reuse across requests.

const db = client.db('ecommerce');

// Get collection references
const usersCollection = db.collection('users');
const ordersCollection = db.collection('orders');
const productsCollection = db.collection('products');

// TypeScript: provide a document type for type safety
// const users = db.collection<UserDocument>('users');

// Collections can also be retrieved inline:
async function getUser(id) {
  return client.db('ecommerce').collection('users').findOne({ _id: id });
}

CRUD Operations: Insert

Insert documents using insertOne() or insertMany(). The driver automatically generates an _id if not provided and returns the inserted IDs. The result object's insertedId (for insertOne) or insertedIds map (for insertMany) let you track what was created. Both operations accept an options object where you can specify writeConcern.

const db = client.db('shop');
const products = db.collection('products');

// Insert one document
const insertResult = await products.insertOne({
  name: 'Mechanical Keyboard',
  price: 149.99,
  category: 'Electronics',
  stock: 50
});
console.log('Inserted ID:', insertResult.insertedId);

// Insert multiple documents
const bulkResult = await products.insertMany([
  { name: 'Mouse Pad', price: 19.99, category: 'Accessories' },
  { name: 'USB Hub', price: 39.99, category: 'Electronics' }
]);
console.log('Inserted count:', bulkResult.insertedCount);

CRUD Operations: Read

Use findOne(filter) to retrieve a single document or find(filter) to get a cursor over all matching documents. Convert a cursor to an array with .toArray() for small result sets, or iterate with for await...of for large ones. Both methods accept a second options argument for projection, sort, skip, limit, and other query modifiers.

const users = db.collection('users');

// Find a single user by email
const user = await users.findOne(
  { email: 'alice@example.com' },
  { projection: { password: 0 } }  // exclude sensitive fields
);

// Find multiple documents with options
const activeUsers = await users.find(
  { active: true, role: 'admin' },
  {
    projection: { name: 1, email: 1 },
    sort: { createdAt: -1 },
    limit: 50
  }
).toArray();

// Iterate large results without loading all into memory
const cursor = users.find({ active: true });
for await (const user of cursor) {
  await processUser(user);
}

CRUD Operations: Update

Update documents with updateOne(), updateMany(), or findOneAndUpdate(). The filter selects which documents to update and the update document specifies the changes using operators like $set, $inc, and $push. updateOne() and updateMany() return a result with matchedCount and modifiedCount. Use upsert: true to create the document if it does not exist.

const orders = db.collection('orders');

// Update one order's status
const updateResult = await orders.updateOne(
  { _id: orderId },
  { $set: { status: 'shipped', shippedAt: new Date() } }
);
console.log('Modified:', updateResult.modifiedCount);

// Atomic: find, update, and return the updated document
const updatedOrder = await orders.findOneAndUpdate(
  { _id: orderId },
  { $set: { status: 'delivered' }, $push: { statusHistory: { status: 'delivered', at: new Date() } } },
  { returnDocument: 'after' }  // return the document AFTER update
);

CRUD Operations: Delete

Delete documents using deleteOne() or deleteMany(). The filter determines which documents to remove. The result contains deletedCount. For safety in production, always test your filter with a find first before running deleteMany—accidentally deleting all documents in a collection is a common and catastrophic mistake. Consider soft deletes (setting a deletedAt field) instead of physical deletion for audit trails.

const sessions = db.collection('sessions');

// Delete one session
const deleteResult = await sessions.deleteOne({ _id: sessionId });
console.log('Deleted:', deleteResult.deletedCount);

// Delete all expired sessions — test filter first!
const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);

// 1. Test: how many would be deleted?
const count = await sessions.countDocuments({ expiresAt: { $lt: thirtyDaysAgo } });
console.log('Would delete:', count);

// 2. Only delete after confirming the count looks right
if (count < 10000) { // sanity check
  await sessions.deleteMany({ expiresAt: { $lt: thirtyDaysAgo } });
}

Running Aggregation Pipelines

Execute aggregation pipelines using collection.aggregate(pipeline). This returns a cursor that you convert to an array or iterate. Pipeline stages are passed as an array of objects. The driver sends the pipeline to MongoDB's aggregation engine and streams back results. For very large result sets, iterate the cursor directly rather than calling .toArray() to avoid loading everything into memory at once.

const orders = db.collection('orders');

// Revenue report by category
const report = await orders.aggregate([
  { $match: { status: 'completed', createdAt: { $gte: new Date('2024-01-01') } } },
  { $group: { _id: '$category', total: { $sum: '$amount' }, count: { $sum: 1 } } },
  { $sort: { total: -1 } }
]).toArray();

console.log('Revenue report:', report);

// For large aggregations, iterate the cursor:
const cursor = orders.aggregate([...largeComplexPipeline]);
for await (const doc of cursor) {
  await writeToReport(doc);
}

Connection Pool Configuration

The MongoClient maintains a connection pool that automatically manages connections to the server. Key options: maxPoolSize (maximum connections, default 5 per host), minPoolSize (keep-alive minimum), connectTimeoutMS, and socketTimeoutMS. For high-traffic APIs, increase maxPoolSize but balance it against MongoDB's per-connection memory overhead. Monitor pool utilization using Atlas metrics.

const client = new MongoClient(uri, {
  maxPoolSize: 20,           // max 20 connections to the server
  minPoolSize: 5,            // keep at least 5 connections warm
  connectTimeoutMS: 5000,   // fail fast if can't connect in 5s
  socketTimeoutMS: 45000,   // idle socket timeout
  serverSelectionTimeoutMS: 5000  // how long to wait to find an available server
});

Quick Check

Test your understanding of MongoDB & NoSQL Databases concepts from this lesson.

Lesson Recap

In this lesson you learned: create one MongoClient at startup and reuse it as a singleton — never create one per request, get database and collection references with client.db() and db.collection() — these are lightweight object references, and use insertOne/insertMany, findOne/find, updateOne/updateMany/findOneAndUpdate, and deleteOne/deleteMany for CRUD operations. Next up we explore Mongoose schemas, models, and virtuals as a higher-level abstraction over the native driver.

Frequently asked questions

Is the “Connecting With the Official Node.js Driver” lesson free?

Yes — the full text of “Connecting With the Official Node.js Driver” is free to read here on the web, and the MongoDB Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the MongoDB Academy course, upgrade to CoddyKit PRO.

What will I learn in “Connecting With the Official Node.js Driver”?

Learners will create a MongoClient, manage the connection pool, and run CRUD operations from a Node.js application using the native driver. You practise MongoDB Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start MongoDB Academy?

No prior experience is required. MongoDB Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 1 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Connecting With the Official Node.js Driver” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this MongoDB Academy lesson?

Yes. Every MongoDB Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Connecting With the Official Node.js Driver
  2. Mongoose Schemas, Models, and Virtuals
  3. Mongoose Queries, Chaining, and Lean Documents
  4. Mongoose Middleware: Pre and Post Hooks
← Back to MongoDB Academy