Mongoose: How to connect to multiple databases

Updated: December 30, 2023 By: Guest Contributor Post a comment

Introduction

When developing applications using Node.js with a MongoDB backend, there may come a time when you need to access multiple databases. Leveraging the popular ODM (Object Document Mapper) library Mongoose makes handling connections straightforward, yet connecting to multiple databases can seem daunting. In this tutorial, we aim to simplify that process and ensure you have the know-how to handle this with ease.

We will start from the basics of setting up a single database connection and advance toward connecting to and managing multiple databases simultaneously. You will learn through conclusive examples using modern JavaScript/TypeScript syntax such as arrow functions and the powerful async/await pattern for handling asynchronous operations.

Prerequisites: Before continuing with this tutorial, please ensure you have Node.js installed, a basic understanding of MongoDB, and familiarity with Mongoose.

Setting Up a Mongoose Connection

// Import mongoose
const mongoose = require('mongoose');

// Connect to the database
mongoose.connect('mongodb://localhost:27017/myapp', { useNewUrlParser: true, useUnifiedTopology: true });

We have just set up a basic mongoose connection to a local MongoDB database called myapp. It’s as easy as calling mongoose.connect with the MongoDB URI.

This establishes a single database connection which suffices in many applications. However, things get a bit more complex when your application requires connections to multiple databases.

Connection to Multiple Databases

Let’s expand upon our setup to include connecting to multiple databases. We will change our approach and create separate connection instances for each database.

// Create connection instances
const db1 = mongoose.createConnection('mongodb://localhost:27017/db1', { useNewUrlParser: true, useUnifiedTopology: true });
const db2 = mongoose.createConnection('mongodb://localhost:27017/db2', { useNewUrlParser: true, useUnifiedTopology: true });

With these two instances db1 and db2, we can now interact with two different databases. The connections are independent of each other and do not interfere.

Next, we’ll look at how to handle models with these connections which will allow us to interact with the databases’ respective collections.

Models and Multiple Databases

When working with Mongoose, you often define models which are constructors we compile from Schema definitions and represent documents in a MongoDB collection.

For connecting these models to their respective databases, we define them through the model() method of the connection instance:

// Define Schemas
class MyModel1 extends mongoose.Schema {
 constructor() {
 super({
 name: String,
 age: Number
 });
 }
}

class MyModel2 extends mongoose.Schema {
 constructor() {
 super({
 itemName: String,
 price: Number
 });
 }
}
// Create models
class MyModel1 extends mongoose.Model {
 constructor(db1) {
 super(db1, new MyModel1(), 'collection1');
 }
}

class MyModel2 extends mongoose.Model {
 constructor(db2) {
 super(db2, new MyModel2(), 'collection2');
 }
}
// The 'collection1' and 'collection2' are names of the collections in the MongoDB 'db1' and 'db2' respectively.

With these models, you can now read and write documents to your databases using your connections db1 and db2.

Error Handling with Multiple Databases

As with any database connection, you should also handle connection errors elegantly. To do this, listen to error events on each connection:

db1.on('error', console.error.bind(console, 'connection error:'));
db1.once('open', () => {
 console.log('Connected to DB1');
});

db2.on('error', console.error.bind(console, 'connection error:'));
db2.once('open', () => {
 console.log('Connected to DB2');
});

Event handling allows you to manage the connection state effectively.

Usage with Async/Await

The async/await syntax in ES7 has significantly improved the writing and reading of asynchronous JavaScript codes. Here’s how we can use it with Mongoose:

const connectToDatabase = async (uri) => {
 try {
 const connection = await mongoose.createConnection(uri, { useNewUrlParser: true, useUnifiedTopology: true });
 return connection;
 } catch (error) {
 console.error('Connection error:', error);
 }
};

const db1ConnectionPromise = connectToDatabase('mongodb://localhost:27017/db1');
const db2ConnectionPromise = connectToDatabase('mongodb://localhost:27017/db2');

// later in your code, when you need to use the connections, you use await
const db1 = await db1ConnectionPromise;
class MyModel1 extends mongoose.Model {
 constructor() {
 super(db1, new MyModel1(), 'collection1');
 }
}
const db2 = await db2ConnectionPromise;
class MyModel2 extends mongoose.Model {
 constructor() {
 super(db2, new MyModel2(), 'collection2');
 }
}

By defining the class directly inside an async function, you ensure that the model can only be used once the associated connection has been established successfully.

Conclusion

In conclusion, Mongoose provides a straightforward way to connect to multiple databases in a Node.js application. We’ve described how to set up connections, define models associated with each database, handle errors, and use them within async contexts. Employing modern JavaScript practices makes the code organized, readable, and easier to maintain. So now, as your applications grow and need to access more than one database, applying these practices can ensure that your database logic remains solid and scalable.