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.