Explore Developer Center's New Chatbot! MongoDB AI Chatbot can be accessed at the top of your navigation to answer all your MongoDB questions.

Join us at AWS re:Invent 2024! Learn how to use MongoDB for AI use cases.
MongoDB Developer
Atlas
plus
Sign in to follow topics
MongoDB Developer Centerchevron-right
Developer Topicschevron-right
Productschevron-right
Atlaschevron-right

Multiple MongoDB Connections in a Single Application

Vishal Turi10 min read • Published Nov 08, 2023 • Updated Apr 02, 2024
Node.jsAtlasJavaScript
Facebook Icontwitter iconlinkedin icon
Rate this tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
MongoDB, a popular NoSQL database, is widely used in various applications and scenarios. While a single database connection can adequately serve the needs of numerous projects, there are specific scenarios and various real-world use cases that highlight the advantages of employing multiple connections.
In this article, we will explore the concept of establishing multiple MongoDB connections within a single Node.js application.

Exploring the need for multiple MongoDB connections: Use cases & examples

In the world of MongoDB and data-driven applications, the demand for multiple MongoDB connections is on the rise. Let's explore why this need arises and discover real-world use cases and examples where multiple connections provide a vital solution.
Sectors such as e-commerce, gaming, financial services, media, entertainment, and the Internet of Things (IoT) frequently contend with substantial data volumes or data from diverse sources.
For instance, imagine a web application that distributes traffic evenly across several MongoDB servers using multiple connections or a microservices architecture where each microservice accesses the database through its dedicated connection. Perhaps in a data processing application, multiple connections allow data retrieval from several MongoDB servers simultaneously. Even a backup application can employ multiple connections to efficiently back up data from multiple MongoDB servers to a single backup server.
Moreover, consider a multi-tenant application where different tenants or customers share the same web application but require separate, isolated databases. In this scenario, each tenant can have their own dedicated MongoDB connection. This ensures data separation, security, and customization for each tenant while all operating within the same application. This approach simplifies management and provides an efficient way to scale as new tenants join the platform without affecting existing ones.
Before we delve into practical implementation, let's introduce some key concepts that will be relevant in the upcoming use case examples. Consider various use cases such as load balancing, sharding, read replicas, isolation, and fault tolerance. These concepts play a crucial role in scenarios where multiple MongoDB connections are required for efficient data management and performance optimization.

Prerequisites

Throughout this guide, we'll be using Node.js, Express.js, and the Mongoose NPM package for managing MongoDB interactions. Before proceeding, ensure that your development environment is ready and that you have these dependencies installed.
If you are new to MongoDB or haven't set up MongoDB before, the first step is to set up a MongoDB Atlas account. You can find step-by-step instructions on how to do this in the MongoDB Getting Started with Atlas article.
This post uses MongoDB 6.3.2 and Node.js 18.17.1
If you're planning to create a new project, start by creating a fresh directory for your project. Then, initiate a new project using the npm init command.
If you already have an existing project and want to integrate these dependencies, ensure you have the project's directory open. In this case, you only need to install the dependencies Express and Mongoose if you haven’t already, making sure to specify the version numbers to prevent any potential conflicts.
1npm i express@4.18.2 mongoose@7.5.3
Please be aware that Mongoose is not the official MongoDB driver but a popular Object Data Modelling (ODM) library for MongoDB. If you prefer to use the official MongoDB driver, you can find relevant documentation on the MongoDB official website.
The next step is to set up the environment .env file if you haven't already. We will define variables for the MongoDB connection strings that we will use throughout this article. The PRIMARY_CONN_STR variable is for the primary MongoDB connection string, and the SECONDARY_CONN_STR variable is for the secondary MongoDB connection string.
1PRIMARY_CONN_STR=mongodb+srv://…
2SECONDARY_CONN_STR=mongodb+srv://…
If you are new to MongoDB and need guidance on obtaining a MongoDB connection string from Atlas, please refer to the Get Connection String article.
Now, we'll break down the connection process into two parts: one for the primary connection and the other for the secondary connection.
Now, let's begin by configuring the primary connection.

Setting up the primary MongoDB connection

The primary connection process might be familiar to you if you've already implemented it in your application. However, I'll provide a detailed explanation for clarity. Readers who are already familiar with this process can skip this section.
We commonly utilize the mongoose.connect() method to establish the primary MongoDB database connection for our application, as it efficiently manages a single connection pool for the entire application.
In a separate file named db.primary.js, we define a connection method that we'll use in our main application file (for example, index.js). This method, shown below, configures the MongoDB connection and handles events:
1const mongoose = require("mongoose");
2
3module.exports = (uri, options = {}) => {
4 // By default, Mongoose skips properties not defined in the schema (strictQuery). Adjust it based on your configuration.
5 mongoose.set('strictQuery', true);
6
7 // Connect to MongoDB
8 mongoose.connect(uri, options)
9 .then()
10 .catch(err => console.error("MongoDB primary connection failed, " + err));
11
12 // Event handling
13 mongoose.connection.once('open', () => console.info("MongoDB primary connection opened!"));
14 mongoose.connection.on('connected', () => console.info("MongoDB primary connection succeeded!"));
15 mongoose.connection.on('error', (err) => {
16 console.error("MongoDB primary connection failed, " + err);
17 mongoose.disconnect();
18 });
19 mongoose.connection.on('disconnected', () => console.info("MongoDB primary connection disconnected!"));
20
21 // Graceful exit
22 process.on('SIGINT', () => {
23 mongoose.connection.close().then(() => {
24 console.info("Mongoose primary connection disconnected through app termination!");
25 process.exit(0);
26 });
27 });
28}
The next step is to create schemas for performing operations in your application. We will write the schema in a separate file named product.schema.js and export it. Let's take an example schema for products in a stores application:
1const mongoose = require("mongoose");
2
3module.exports = (options = {}) => {
4 // Schema for Product
5 return new mongoose.Schema(
6 {
7 store: {
8 _id: mongoose.Types.ObjectId, // Reference-id to the store collection
9 name: String
10 },
11 name: String
12 // add required properties
13 },
14 options
15 );
16}
Now, let’s import the db.primary.js file in our main file (for example, index.js) and use the method defined there to establish the primary MongoDB connection. You can also pass an optional connection options object if needed.
After setting up the primary MongoDB connection, you import the product.schema.js file to access the Product Schema. This enables you to create a model and perform operations related to products in your application:
1// Primary Connection (Change the variable name as per your .env configuration!)
2// Establish the primary MongoDB connection using the connection string variable declared in the Prerequisites section.
3require("./db.primary.js")(process.env.PRIMARY_CONN_STR, {
4 // (optional) connection options
5});
6
7// Import Product Schema
8const productSchema = require("./product.schema.js")({
9 collection: "products",
10 // Pass configuration options if needed
11});
12
13// Create Model
14const ProductModel = mongoose.model("Product", productSchema);
15
16// Execute Your Operations Using ProductModel Object
17(async function () {
18 let product = await ProductModel.findOne();
19 console.log(product);
20})();
Now, let's move on to setting up a secondary or second MongoDB connection for scenarios where your application requires multiple MongoDB connections.

Setting up secondary MongoDB connections

Depending on your application's requirements, you can configure secondary MongoDB connections for various use cases. But before that, we'll create a connection code in a db.secondary.js file, specifically utilizing the mongoose.createConnection() method. This method allows us to establish separate connection pools each tailored to a specific use case or data access pattern, unlike the mongoose.connect() method that we used previously for the primary MongoDB connection:
1const mongoose = require("mongoose");
2
3module.exports = (uri, options = {}) => {
4 // Connect to MongoDB
5 const db = mongoose.createConnection(uri, options);
6
7 // By default, Mongoose skips properties not defined in the schema (strictQuery). Adjust it based on your configuration.
8 db.set('strictQuery', true);
9
10 // Event handling
11 db.once('open', () => console.info("MongoDB secondary connection opened!"));
12 db.on('connected', () => console.info(`MongoDB secondary connection succeeded!`));
13 db.on('error', (err) => {
14 console.error(`MongoDB secondary connection failed, ` + err);
15 db.close();
16 });
17 db.on('disconnected', () => console.info(`MongoDB secondary connection disconnected!`));
18
19 // Graceful exit
20 process.on('SIGINT', () => {
21 db.close().then(() => {
22 console.info(`Mongoose secondary connection disconnected through app termination!`);
23 process.exit(0);
24 });
25 });
26
27 // Export db object
28 return db;
29}
Now, let’s import the db.secondary.js file in our main file (for example, index.js), create the connection object with a variable named db, and use the method defined there to establish the secondary MongoDB connection. You can also pass an optional connection options object if needed:
1// Secondary Connection (Change the variable name as per your .env configuration!)
2// Establish the secondary MongoDB connection using the connection string variable declared in the Prerequisites section.
3const db = require("./db.secondary.js")(process.env.SECONDARY_CONN_STR, {
4 // (optional) connection options
5});
Now that we are all ready with the connection, you can use that db object to create a model. We explore different scenarios and examples to help you choose the setup that best aligns with your specific data access and management needs:

1. Using the existing schema

You can choose to use the same schema product.schema.js file that was employed in the primary connection. This is suitable for scenarios where both connections will operate on the same data model.
Import the product.schema.js file to access the Product Schema. This enables you to create a model using db object and perform operations related to products in your application:
1// Import Product Schema
2const secondaryProductSchema = require("./product.schema.js")({
3 collection: "products",
4 // Pass configuration options if needed
5});
6
7// Create Model
8const SecondaryProductModel = db.model("Product", secondaryProductSchema);
9
10// Execute Your Operations Using SecondaryProductModel Object
11(async function () {
12 let product = await SecondaryProductModel.findOne();
13 console.log(product);
14})();
To see a practical code example and available resources for using the existing schema of a primary database connection into a secondary MongoDB connection in your project, visit the GitHub repository.

2. Setting schema flexibility

When working with multiple MongoDB connections, it's essential to have the flexibility to adapt your schema based on specific use cases. While the primary connection may demand a strict schema with validation to ensure data integrity, there are scenarios where a secondary connection serves a different purpose. For instance, a secondary connection might store data for analytics on an archive server, with varying schema requirements driven by past use cases. In this section, we'll explore how to configure schema flexibility for your secondary connection, allowing you to meet the distinct needs of your application.
If you prefer to have schema flexibility in mongoose, you can pass the strict: false property in the options when configuring your schema for the secondary connection. This allows you to work with data that doesn't adhere strictly to the schema.
Import the product.schema.js file to access the Product Schema. This enables you to create a model using db object and perform operations related to products in your application:
1// Import Product Schema
2const secondaryProductSchema = require("./product.schema.js")({
3 collection: "products",
4 strict: false
5 // Pass configuration options if needed
6});
7
8// Create Model
9const SecondaryProductModel = db.model("Product", secondaryProductSchema);
10
11// Execute Your Operations Using SecondaryProductModel Object
12(async function () {
13 let product = await SecondaryProductModel.findOne();
14 console.log(product);
15})();
To see a practical code example and available resources for setting schema flexibility in a secondary MongoDB connection in your project, visit the GitHub repository.

3. Switching databases within the same connection

Within your application's database setup, you can seamlessly switch between different databases using the db.useDb() method. This method enables you to create a new connection object associated with a specific database while sharing the same connection pool.
This approach allows you to efficiently manage multiple databases within your application, using a single connection while maintaining distinct data contexts for each database.
Import the product.schema.js file to access the Product Schema. This enables you to create a model using db object and perform operations related to products in your application.
Now, to provide an example where a store can have its own database containing users and products, you can include the following scenario.
Example use case: Store with separate database
Imagine you're developing an e-commerce platform where multiple stores operate independently. Each store has its database to manage its products. In this scenario, you can use the db.useDb() method to switch between different store databases while maintaining a shared connection pool:
1// Import Product Schema
2const secondaryProductSchema = require("./product.schema.js")({
3 collection: "products",
4 // strict: false // that doesn't adhere strictly to the schema!
5 // Pass configuration options if needed
6});
7
8// Create a connection for 'Store A'
9const storeA = db.useDb('StoreA');
10
11// Create Model
12const SecondaryStoreAProductModel = storeA.model("Product", secondaryProductSchema);
13
14// Execute Your Operations Using SecondaryStoreAProductModel Object
15(async function () {
16 let product = await SecondaryStoreAProductModel.findOne();
17 console.log(product);
18})();
19
20// Create a connection for 'Store B'
21const storeB = db.useDb('StoreB');
22
23// Create Model
24const SecondaryStoreBProductModel = storeB.model("Product", secondaryProductSchema);
25
26// Execute Your Operations Using SecondaryStoreBProductModel Object
27(async function () {
28 let product = await SecondaryStoreBProductModel.findOne();
29 console.log(product);
30})();
In this example, separate database connections have been established for Store A and Store B, each containing its product data. This approach provides a clear separation of data while efficiently utilizing a single shared connection pool for all stores, enhancing data management in a multi-store e-commerce platform.
In the previous section, we demonstrated a static approach where connections were explicitly created for each store, and each connection was named accordingly (e.g., StoreA, StoreB).
To introduce a dynamic approach, you can create a function that accepts a store's ID or name as a parameter and returns a connection object. This dynamic function allows you to switch between different stores by providing their identifiers, and it efficiently reuses existing connections when possible.
1// Function to get connection object for particular store's database
2function getStoreConnection(storeId) {
3 return db.useDb("Store"+storeId, { useCache: true });
4}
5
6// Create a connection for 'Store A'
7const store = getStoreConnection("A");
8
9// Create Model
10const SecondaryStoreProductModel = store.model("Product", secondaryProductSchema);
11
12// Execute Your Operations Using SecondaryStoreProductModel Object
13(async function () {
14 let product = await SecondaryStoreProductModel.findOne();
15 console.log(product);
16})();
In the dynamic approach, connection instances are created and cached as needed, eliminating the need for manually managing separate connections for each store. This approach enhances flexibility and resource efficiency in scenarios where you need to work with multiple stores in your application.
By exploring these examples, we've covered a range of scenarios for managing multiple databases within the same connection, providing you with the flexibility to tailor your database setup to your specific application needs. You're now equipped to efficiently manage distinct data contexts for various use cases within your application.
To see a practical code example and available resources for switching databases within the same connection into a secondary MongoDB connection in your project, visit the GitHub repository.

Best practices

In the pursuit of a robust and efficient MongoDB setup within your Node.js application, I recommend the following best practices. These guidelines serve as a foundation for a reliable implementation, and I encourage you to consider and implement them:
  • Connection pooling: Make the most of connection pooling to efficiently manage MongoDB connections, enabling connection reuse and reducing overhead. Read more about connection pooling.
  • Error handling: Robust error-handling mechanisms, comprehensive logging, and contingency plans ensure the reliability of your MongoDB setup in the face of unexpected issues.
  • Security: Prioritize data security with authentication, authorization, and secure communication practices, especially when dealing with sensitive information. Read more about MongoDB Security.
  • Scalability: Plan for scalability from the outset, considering both horizontal and vertical scaling strategies to accommodate your application's growth.
  • Testing: Comprehensive testing in various scenarios, such as failover, high load, and resource constraints, validates the resilience and performance of your multiple MongoDB connection setup.

Conclusion

Leveraging multiple MongoDB connections in a Node.js application opens up a world of possibilities for diverse use cases, from e-commerce to multi-tenant systems. Whether you need to enhance data separation, scale your application efficiently, or accommodate different data access patterns, these techniques empower you to tailor your database setup to the unique needs of your project. With the knowledge gained in this guide, you're well-prepared to manage multiple data contexts within a single application, ensuring robust, flexible, and efficient MongoDB interactions.

Additional resources

  • Mongoose documentation: For an in-depth understanding of Mongoose connections, explore the official Mongoose documentation.
  • GitHub repository: To dive into the complete implementation of multiple MongoDB connections in a Node.js application that we have performed above, visit the GitHub repository. Feel free to clone the repository and experiment with different use cases in your projects.
If you have any questions or feedback, check out the MongoDB Community Forums and let us know what you think.

Facebook Icontwitter iconlinkedin icon
Rate this tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
Related
Tutorial

How to Manage Data at Scale With MongoDB Atlas Online Archive


Sep 23, 2022 | 6 min read
Tutorial

How to Optimize LLM Applications With Prompt Compression Using LLMLingua and LangChain


Jun 18, 2024 | 13 min read
Article

Deploying MongoDB Atlas With Terraform with Azure


Jun 18, 2024 | 7 min read
Article

How to Enable Local and Automatic Testing of Atlas Search-Based Features


Jun 12, 2024 | 8 min read
Table of Contents