NodeJs
MongoDB
Database Connection
Code Optimization
Software Development

How to properly reuse connection to Mongodb across NodeJs application and modules

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

Connecting to a MongoDB database in a Node.js application is straightforward with the help of the MongoDB Node.js Driver. However, if connections are not managed properly, you can run into performance issues, overwhelmed resources, or even application crashes. This article will guide you through the best practices for reusing MongoDB connections across a Node.js application, ensuring efficient resource utilization and improved performance.

Why Connection Reuse Matters

Each connection to a MongoDB database takes up resources on both the client and the MongoDB server. Opening and closing connections frequently can lead to resource exhaustion and significant overhead. Therefore, reusing connections allows you to:

  • Improve performance: Reduce latency by reusing already established connections.
  • Use resources efficiently: Limit the number of open connections, thus avoiding running out of file descriptors or hitting MongoDB connection limits.
  • Optimize application stability: Ensure that your application does not fail under load due to excessive connection attempts.

Approach to Reusing MongoDB Connections

The Connection Pool

MongoDB's Node.js driver automatically maintains a pool of connections. When you connect to the database, a pool of connections is created, and these connections are reused for subsequent database operations. By default, the pool size is set to 5. This means that up to 5 connections are established in the pool and reused as needed.

javascript
1const { MongoClient } = require('mongodb');
2const uri = "mongodb://localhost:27017";
3const client = new MongoClient(uri, { useNewUrlParser: true, useUnifiedTopology: true });
4
5async function connect() {
6  await client.connect();
7  console.log("Connected successfully to MongoDB");
8}
9
10module.exports = { client, connect };

Singleton Pattern

To reuse the connection across various parts of your application, it's critical to maintain a single instance of the MongoDB client. This can be achieved using a singleton pattern.

javascript
1let dbInstance = null;
2
3async function getDbInstance() {
4  if (!dbInstance) {
5    const client = new MongoClient(uri, { useNewUrlParser: true, useUnifiedTopology: true });
6    await client.connect();
7    dbInstance = client.db('myDatabase');
8    console.log("Initialized new database instance.");
9  }
10  return dbInstance;
11}
12
13module.exports = getDbInstance;

Example Usage in Different Modules

By using the singleton pattern, you ensure that different modules within your application can access the same database connection. Here's how you can use the getDbInstance function in a different module:

javascript
1const getDbInstance = require('./dbInstance');
2
3async function fetchData() {
4  const db = await getDbInstance();
5  const collection = db.collection('myCollection');
6  const data = await collection.find({}).toArray();
7  return data;
8}
9
10module.exports = fetchData;

Gracefully Closing Connections

When your application shuts down, it's crucial to close all connections gracefully to free resources. You can listen to the process events to ensure connections are closed when the application exits:

javascript
1process.on('SIGINT', async () => {
2  console.log("Shutting down...");
3  await client.close();
4  console.log("Disconnected from MongoDB");
5  process.exit(0);
6});

Summary Table

Key ConceptDescription
Connection PoolSets up a pool of connections that are reused, reducing the overhead of establishing new connections. Default pool size is 5.
Singleton PatternEnsures a single MongoDB client instance is used throughout the application, minimizing resource usage.
Graceful ShutdownInvolves listening to process termination events to close connections cleanly when the application exits.

Additional Considerations

Pool Size Configuration

While the default pool size is enough for many applications, you might need to adjust this based on your application's specific needs by using the poolSize option:

javascript
const client = new MongoClient(uri, { useNewUrlParser: true, useUnifiedTopology: true, poolSize: 10 });

Error Handling

Proper error handling is essential to maintain a stable connection, especially for database operations. Always handle exceptions and provide fallbacks where necessary:

javascript
1try {
2  const db = await getDbInstance();
3  const collection = db.collection('myCollection');
4  const data = await collection.find({}).toArray();
5} catch (error) {
6  console.error("Failed to fetch data:", error);
7}

Conclusion

Effectively reusing MongoDB connections in a Node.js application requires understanding how the MongoDB Node.js Driver handles connections and employing design patterns such as a singleton. By properly configuring and managing connections, you can build applications that are both efficient and reliable. By following these practices, developers can avoid common pitfalls related to resource exhaustion or high latency, thus providing a smooth and responsive experience to users.


Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.