Join us Sept 17 at .local NYC! Use code WEB50 to save 50% on tickets. Learn more >
MongoDB Event
Docs Menu
Docs Home
/ / /
Node.js Driver
/

Tutorial: Multiple MongoDB Connections in a Single Application

Most applications use a single MongoDB connection. However, some applications benefit from multiple connections for data separation, performance optimization, or architectural requirements.

In this tutorial, you will learn how to establish multiple MongoDB connections within a single Node.js application.

Multiple MongoDB connections are useful in applications that require data separation, improved performance, or architectural flexibility.

Common use cases for multiple MongoDB connections include the following:

  • Multi-tenant applications: Each tenant uses a separate database with its own connection for data isolation and security

  • Microservices architecture: Each service maintains its own dedicated database connection

  • Load distribution: Distribute read and write operations across multiple database instances

  • Data processing: Retrieve data from multiple MongoDB servers simultaneously for analytics or reporting

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 a way to scale as new tenants join the platform without affecting existing ones.

Consider the following MongoDB concepts as you explore how to implement multiple connections in your application:

  • Load balancing

  • Sharding

  • Read replicas

  • Fault tolerance

These concepts play a role in scenarios where multiple MongoDB connections are required for efficient data management and performance optimization.

In this tutorial, you will perform the following actions:

  • Set up your environment and install the required dependencies

  • Connect to MongoDB

  • Set up the primary MongoDB connection

  • Set up secondary MongoDB connections

  • Use an existing schema

  • Set schema flexibility

  • Switch databases within the same connection

Before you begin this tutorial, you must have a MongoDB Atlas cluster. To learn how to create a free MongoDB Atlas cluster, see the Get Started with Atlas tutorial.

Note

This tutorial uses MongoDB Server 8.0.12 and Node.js version 20.15.1.

1

Create a new directory for your project and initialize a new Node.js project:

mkdir mongodb-multiple-connections
cd mongodb-multiple-connections
npm init -y
2

Install the required dependencies for this tutorial:

npm install express mongoose

This tutorial uses Express.js for the web framework and Mongoose for MongoDB object modeling.

Note

This tutorial uses Mongoose, a popular Object Data Modeling (ODM) library for MongoDB. For more information on getting started with Mongoose, see the Get Started with Mongoose tutorial.

3

Create a .env file in your project root directory to store your MongoDB connection strings:

touch .env

Add the following environment variables to your .env file:

PRIMARY_CONN_STR=<your-primary-connection-string>
SECONDARY_CONN_STR=<your-secondary-connection-string>

Replace the placeholder values with your actual MongoDB Atlas connection strings. To learn how to find your MongoDB Atlas connection string, see the Connect to Your Cluster tutorial.

Install the dotenv package to load your environment variables:

npm install dotenv

You now have a project that is ready to set up multiple MongoDB connections in your application.

When you complete this tutorial, your project directory for this application will have the following structure:

mongodb-multiple-connections/
├── .env # Environment variables
├── package.json # Project dependencies
├── package-lock.json # Dependency lock file
├── node_modules/ # Installed packages
├── index.js # Main application file
├── db.primary.js # Primary connection configuration
├── db.secondary.js # Secondary connection configuration
└── product.schema.js # Product schema definition

The key files you'll create in this tutorial serve the following purposes:

  • index.js: Your main application file that imports and uses both connections

  • db.primary.js: Configures the primary MongoDB connection using mongoose.connect()

  • db.secondary.js: Configures secondary MongoDB connections using mongoose.createConnection()

  • product.schema.js: Defines the reusable product schema for both connections

  • .env: Stores your MongoDB connection strings securely

In this section, learn how to set up the primary MongoDB connection in your application by using Mongoose. Use the mongoose.connect() method to establish the primary MongoDB database connection for your application. This method manages a single connection pool for the entire application.

1

In a new file named db.primary.js, define a connection method to use in your main application file, index.js. This method configures the MongoDB connection and handles events. Copy and paste the following code into the db.primary.js file:

require('dotenv').config();
const mongoose = require("mongoose");
module.exports = async (uri, options = {}) => {
// By default, Mongoose skips properties not defined in the schema (strictQuery).
// You can adjust it based on your configuration.
mongoose.set('strictQuery', true);
// Connect to MongoDB
try {
await mongoose.connect(uri, options);
console.info("MongoDB primary connection initiated");
} catch (err) {
console.error("MongoDB primary connection failed, " + err);
}
// Event handling
mongoose.connection.once('open', () => console.info("MongoDB primary connection opened!"));
mongoose.connection.on('connected', () => console.info("MongoDB primary connection succeeded!"));
mongoose.connection.on('error', (err) => {
console.error("MongoDB primary connection failed, " + err);
mongoose.disconnect();
});
mongoose.connection.on('disconnected', () => console.info("MongoDB primary connection disconnected!"));
// Graceful exit
process.on('SIGINT', async () => {
try {
await mongoose.connection.close();
console.info("Mongoose primary connection disconnected through app termination!");
process.exit(0);
} catch (err) {
console.error("Error during graceful shutdown:", err);
process.exit(1);
}
});
}
2

Create schemas for performing operations in your application. Write the schema in a separate file named product.schema.js and export it, as shown in the following code:

const mongoose = require("mongoose");
module.exports = (options = {}) => {
// Schema for Product
return new mongoose.Schema(
{
store: {
_id: mongoose.Types.ObjectId, // Reference-id to the store collection
name: String
},
name: String,
price: Number,
category: String,
description: String
// add required properties
},
options
);
}
3

Import the db.primary.js file in your main index.js file and use the method defined there to establish the primary MongoDB connection. You can also pass an optional connection options object if needed.

Then, import the product.schema.js file to access the product schema. This lets you create a model and perform operations related to products in your application. Copy and paste the following code into your index.js file to set up the primary connection and perform operations on sample product data:

// Load environment variables
require('dotenv').config();
const mongoose = require("mongoose");
// Async function to establish the primary MongoDB connection
async function establishPrimaryConnection() {
try {
await require("./db.primary.js")(process.env.PRIMARY_CONN_STR, {
// (optional) connection options
});
} catch (error) {
console.error('Failed to establish primary connection:', error);
process.exit(1);
}
}
// Initialize connection
establishPrimaryConnection();
// Import Product Schema
const productSchema = require("./product.schema.js")({
collection: "products",
// Pass configuration options if needed
});
// Create Model
const ProductModel = mongoose.model("Product", productSchema);
// Sample products data
const sampleProducts = [
{
name: "Laptop Pro",
price: 1299.99,
category: "Electronics",
description: "High-performance laptop for professionals"
},
{
name: "Wireless Headphones",
price: 199.99,
category: "Electronics",
description: "Premium noise-cancelling headphones"
},
{
name: "Coffee Maker",
price: 89.99,
category: "Kitchen",
description: "Automatic drip coffee maker"
}
];
// Wait for connection to be ready before executing operations
mongoose.connection.once('open', async () => {
console.log('Primary database connected, executing operations...');
try {
// Check if products already exist
const existingCount = await ProductModel.countDocuments();
console.log(`Existing products in primary DB: ${existingCount}`);
if (existingCount === 0) {
console.log('Inserting sample products into primary database...');
await ProductModel.insertMany(sampleProducts);
console.log('Sample products inserted into primary database!');
}
// Find and display a product
let product = await ProductModel.findOne();
console.log('Product found in primary DB:', product);
// Display all products
const allProducts = await ProductModel.find();
console.log(`Total products in primary DB: ${allProducts.length}`);
} catch (error) {
console.error('Error with primary database operations:', error);
}
});
Primary database connected, executing operations...
MongoDB primary connection initiated
Existing products in primary DB: 3
Product found in primary DB: {
_id: new ObjectId('...'),
name: 'Laptop Pro',
__v: 0
}
Total products in primary DB: 3

where your application requires multiple MongoDB connections.

You can configure secondary MongoDB connections for various use cases. In this section, learn how to set up and use the secondary connection.

1

Create a connection code in a db.secondary.js file by using the mongoose.createConnection() method. This method allows you to establish separate connection pools each tailored to a specific use case or data access pattern, unlike the mongoose.connect() method that you used previously for the primary MongoDB connection:

const mongoose = require("mongoose");
module.exports = (uri, options = {}) => {
// Connect to MongoDB
const db = mongoose.createConnection(uri, options);
// By default, Mongoose skips properties not defined in the schema (strictQuery).
// Adjust it based on your configuration.
db.set('strictQuery', true);
// Event handling
db.once('open', () => console.info("MongoDB secondary connection opened!"));
db.on('connected', () => console.info(`MongoDB secondary connection succeeded!`));
db.on('error', (err) => {
console.error(`MongoDB secondary connection failed, ` + err);
db.close();
});
db.on('disconnected', () => console.info(`MongoDB secondary connection disconnected!`));
// Graceful exit
process.on('SIGINT', async () => {
try {
await db.close();
console.info(`Mongoose secondary connection disconnected through app termination!`);
process.exit(0);
} catch (err) {
console.error("Error during graceful shutdown:", err);
process.exit(1);
}
});
// Export db object
return db;
}
2

Import the db.secondary.js file in your main index.js file, 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. Add the following code to the end of the index.js file:

// Load environment variables
require('dotenv').config();
// Establish the secondary MongoDB connection
const db = require("./db.secondary.js")(process.env.SECONDARY_CONN_STR, {
// (optional) connection options
});
// Import Product Schema
const SecondaryProductSchema = require("./product.schema.js")({
collection: "products",
// Pass configuration options if needed
});
// Create Model using the secondary connection
const SecondaryProductModel = db.model("Product", SecondaryProductSchema);
// Sample products data for secondary database
const SecondarySampleProducts = [
{
name: "Smart Watch",
price: 199.99,
category: "Electronics",
description: "Advanced fitness tracking smartwatch"
},
{
name: "Bluetooth Speaker",
price: 79.99,
category: "Electronics",
description: "Portable wireless speaker with premium sound"
},
{
name: "Desk Lamp",
price: 49.99,
category: "Home",
description: "LED desk lamp with adjustable brightness"
}
];
// Wait for secondary connection to be ready before executing operations
db.once('open', async () => {
console.log('Secondary database connected, executing operations...');
try {
// Check if products already exist
const existingCount = await SecondaryProductModel.countDocuments();
console.log(`Existing products in secondary DB: ${existingCount}`);
if (existingCount === 0) {
console.log('Inserting sample products into secondary database...');
await SecondaryProductModel.insertMany(SecondarySampleProducts);
console.log('Sample products inserted into secondary database!');
}
// Find and display a product
let product = await SecondaryProductModel.findOne();
console.log('Product found in secondary DB:', product);
// Display all products
const allProducts = await SecondaryProductModel.find();
console.log(`Total products in secondary DB: ${allProducts.length}`);
} catch (error) {
console.error('Error with secondary database operations:', error);
}
});
Primary database connected, executing operations...
MongoDB primary connection initiated
MongoDB secondary connection succeeded!
MongoDB secondary connection opened!
Secondary database connected, executing operations...
Existing products in primary DB: 3
Existing products in secondary DB: 6
Product found in primary DB: {
_id: new ObjectId('...'),
name: 'Laptop Pro',
__v: 0
}
Product found in secondary DB: {
_id: new ObjectId('...'),
name: 'Smart Watch',
__v: 0
}
Total products in primary DB: 3
Total products in secondary DB: 3

Now that you've set up the connection, you can use the new db object to create a model. The following sections explore different scenarios and examples to help you choose the setup that best aligns with your specific data access and management needs.

If both connections operate on the same data model, use the same product.schema.js file that was employed in the primary connection.

Import the product.schema.js file to access the product schema. This enables you to create a model by using the db object and perform operations related to products in your application:

// Import Product Schema
const secondaryProductSchema = require("./product.schema.js")({
collection: "products",
// Pass configuration options if needed
});
// Create Model
const SecondaryProductModel = db.model("Product", secondaryProductSchema);
// Wait for secondary connection to be ready before executing operations
db.once('open', async () => {
console.log('Secondary database connected, executing operations...');
try {
// Check if products already exist in secondary database
const existingCount = await SecondaryProductModel.countDocuments();
console.log(`Existing products in secondary DB: ${existingCount}`);
if (existingCount === 0) {
console.log('Inserting sample products into secondary database...');
await SecondaryProductModel.insertMany(sampleProducts);
console.log('Sample products inserted into secondary database!');
}
// Find and display a product
let product = await SecondaryProductModel.findOne();
console.log('Product found in secondary DB:', product);
// Display all products
const allProducts = await SecondaryProductModel.find();
console.log(`Total products in secondary DB: ${allProducts.length}`);
} catch (error) {
console.error('Error with secondary database operations:', error);
}
});

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 Using the Existing Schema GitHub repository.

When working with multiple MongoDB connections, it's essential to have the flexibility to adapt your schema based on specific use cases. Although the primary connection might 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, you'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, pass the strict: false property in the schema 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 by using the db object and perform operations related to products in your application:

// Import Product Schema
const secondaryProductSchema = require("./product.schema.js")({
collection: "products",
strict: false
// Pass configuration options if needed
});
// Create Model
const SecondaryProductModel = db.model("Product", secondaryProductSchema);
// Wait for secondary connection to be ready before executing operations
db.once('open', async () => {
console.log('Secondary database (flexible schema) connected, executing operations...');
try {
// Check if products already exist in secondary database
const existingCount = await SecondaryProductModel.countDocuments();
console.log(`Existing products in secondary DB: ${existingCount}`);
if (existingCount === 0) {
// Add extra fields to demonstrate schema flexibility
const flexibleSampleProducts = sampleProducts.map(product => ({
...product,
extraField: "This field is not in the schema but will be saved due to strict: false",
timestamp: new Date()
}));
console.log('Inserting sample products with extra fields into secondary database...');
await SecondaryProductModel.insertMany(flexibleSampleProducts);
console.log('Sample products with extra fields inserted into secondary database!');
}
// Find and display a product
let product = await SecondaryProductModel.findOne();
console.log('Product found in secondary DB (flexible schema):', product);
// Display all products
const allProducts = await SecondaryProductModel.find();
console.log(`Total products in secondary DB: ${allProducts.length}`);
} catch (error) {
console.error('Error with secondary database operations:', error);
}
});

To view a practical code example and available resources for setting schema flexibility in a secondary MongoDB connection in your project, see the Setting Schema Flexibility GitHub repository.

Within your application's database setup, you can switch between different databases by using the db.useDb() method. This method lets you to create a new connection object associated with a specific database while sharing the same connection pool.

This approach allows you to manage multiple databases within your application, by using a single connection while maintaining distinct data contexts for each database.

Import the product.schema.js file to access the product schema. This lets you to create a model by using the db object and perform operations related to products in your application.

Consider an e-commerce platform where multiple stores operate independently, with each store maintaining its own database for product management. Use the db.useDb() method to switch between different store databases while maintaining a shared connection pool, as shown in the following example:

// Load environment variables
require('dotenv').config();
// Establish the secondary MongoDB connection
const db = require("./db.secondary.js")(process.env.SECONDARY_CONN_STR, {
// (optional) connection options
});
// Import Product Schema
const secondaryProductSchema = require("./product.schema.js")({
collection: "products",
// strict: false // that doesn't adhere strictly to the schema!
// Pass configuration options if needed
});
// Base sample products data
const sampleProducts = [
{
name: "Laptop Pro",
price: 1299.99,
category: "Electronics",
description: "High-performance laptop for professionals"
},
{
name: "Wireless Headphones",
price: 199.99,
category: "Electronics",
description: "Premium noise-cancelling headphones"
},
{
name: "Coffee Maker",
price: 89.99,
category: "Kitchen",
description: "Automatic drip coffee maker"
}
];
// Sample store-specific products
const storeAProducts = sampleProducts.map(product => ({
...product,
store: { name: "Store A" },
storeId: "A"
}));
const storeBProducts = [
{
name: "Gaming Chair",
price: 299.99,
category: "Furniture",
description: "Ergonomic gaming chair with RGB lighting",
store: { name: "Store B" },
storeId: "B"
},
{
name: "Mechanical Keyboard",
price: 149.99,
category: "Electronics",
description: "RGB mechanical gaming keyboard",
store: { name: "Store B" },
storeId: "B"
}
];
// Create a connection for 'Store A'
const storeA = db.useDb('StoreA');
// Create Model
const SecondaryStoreAProductModel = storeA.model("Product", secondaryProductSchema);
// Wait for Store A connection to be ready
storeA.once('open', async () => {
console.log('Store A database connected, executing operations...');
try {
// Check if products already exist in Store A
const existingCount = await SecondaryStoreAProductModel.countDocuments();
console.log(`Existing products in Store A: ${existingCount}`);
if (existingCount === 0) {
console.log('Inserting sample products into Store A database...');
await SecondaryStoreAProductModel.insertMany(storeAProducts);
console.log('Sample products inserted into Store A database!');
}
// Find and display a product
let product = await SecondaryStoreAProductModel.findOne();
console.log('Product found in Store A:', product);
// Display all products
const allProducts = await SecondaryStoreAProductModel.find();
console.log(`Total products in Store A: ${allProducts.length}`);
} catch (error) {
console.error('Error with Store A operations:', error);
}
});
// Create a connection for 'Store B'
const storeB = db.useDb('StoreB');
// Create Model
const SecondaryStoreBProductModel = storeB.model("Product", secondaryProductSchema);
// Wait for Store B connection to be ready
storeB.once('open', async () => {
console.log('Store B database connected, executing operations...');
try {
// Check if products already exist in Store B
const existingCount = await SecondaryStoreBProductModel.countDocuments();
console.log(`Existing products in Store B: ${existingCount}`);
if (existingCount === 0) {
console.log('Inserting sample products into Store B database...');
await SecondaryStoreBProductModel.insertMany(storeBProducts);
console.log('Sample products inserted into Store B database!');
}
// Find and display a product
let product = await SecondaryStoreBProductModel.findOne();
console.log('Product found in Store B:', product);
// Display all products
const allProducts = await SecondaryStoreBProductModel.find();
console.log(`Total products in Store B: ${allProducts.length}`);
} catch (error) {
console.error('Error with Store B operations:', error);
}
});

The preceding example establishes separate database connections for Store A and Store B, with each store containing its own product data. This approach provides data separation while utilizing a single shared connection pool for efficient resource management.

The preceding static approach creates explicit connections for each store with predefined names (StoreA, StoreB).

For dynamic store management, create a function that accepts a store identifier as a parameter and returns a connection object. This function enables store switching by identifier and reuses existing connections for improved efficiency.

// Function to get connection object for particular store's database
function getStoreConnection(storeId) {
return db.useDb("Store"+storeId, { useCache: true });
}
// Create a connection for 'Store A'
const store = getStoreConnection("A");
// Create Model
const SecondaryStoreProductModel = store.model("Product", secondaryProductSchema);
// Wait for store connection to be ready
store.once('open', async () => {
console.log('Store A (dynamic) database connected, executing operations...');
try {
// Check if products already exist in the store
const existingCount = await SecondaryStoreProductModel.countDocuments();
console.log(`Existing products in Store A (dynamic): ${existingCount}`);
if (existingCount === 0) {
// Use the same store A products from the previous example
console.log('Inserting sample products into Store A (dynamic) database...');
await SecondaryStoreProductModel.insertMany(storeAProducts);
console.log('Sample products inserted into Store A (dynamic) database!');
}
// Find and display a product
let product = await SecondaryStoreProductModel.findOne();
console.log('Product found in Store A (dynamic):', product);
// Display all products
const allProducts = await SecondaryStoreProductModel.find();
console.log(`Total products in Store A (dynamic): ${allProducts.length}`);
} catch (error) {
console.error('Error with Store A (dynamic) operations:', error);
}
});

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.

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.

This tutorial demonstrates how to implement multiple MongoDB connections in a Node.js application by using Mongoose. You learned to establish primary and secondary connections, implement schema flexibility, and manage multiple databases within a single connection pool.

These techniques enable data separation, improved performance, and architectural flexibility for applications requiring multiple data contexts. You can now implement connection strategies that meet your specific application requirements. Consider the following best practices and additional resources as you continue to work with multiple MongoDB connections.

When implementing multiple MongoDB connections in your Node.js application, follow these best practices:

  • Connection pooling: Use connection pooling to manage MongoDB connections efficiently. Connection pooling enables connection reuse and reduces overhead. To learn more, see Connection Pooling in the Server manual and Manage Connections with Connection Pools in the MongoDB Node.js driver documentation.

  • Error handling: Implement proper error handling, logging, and recovery mechanisms to ensure connection reliability.

  • Security: Implement authentication, authorization, and secure communication practices when handling sensitive data. For more information, see Secure Your Data.

  • Scalability: Design your connection strategy to support both horizontal and vertical scaling requirements.

  • Testing: Test your multiple connection setup under various conditions, including failover scenarios, high load, and resource constraints.

To learn more about getting started with Mongoose, see the Get Started with Mongoose tutorial in the Third-Party Integrations section.

To learn more about using Mongoose with MongoDB, see the Mongoose documentation.

To learn more about the complete implementation of multiple MongoDB connections in a Node.js application, see the GitHub repository.

Back

Connection Troubleshooting

On this page