MongoClient on error event - node.js

I am using Node/Mongo and want to capture all MongoErrors so that I can transform then into another error format. I am looking to do this at the base level but cannot figure it out.
Here's my connection setup:
let connection = false
// Create a Mongo connection
export function getConnection () {
if (!connection) {
connection = MongoClient.connect(config.db.mongo.uri, {
promiseLibrary: Promise //Bluebird
})
}
return connection
}
// Fetch a connection for a collection
export function getCollection (collection) {
return getConnection().then(c =>
c.collection(collection)
)
}
I've tried adding on('error') to both connection and MongoClient as well as MongoClient.Db but they do not have that method. I've additionally added a catch block to my getCollection but the errors do not seem to hit it (I am testing with the MongoError 11000 for duplicate fields.
It seems that it can be done but haven't figured it out. It may be because I am using promises.

This snippet of code does it for us. I suspect you need to pass in the function that handles the 'error' event as well. Hope it helps someone who stumbles across this question in the future.
MongoClient.connect(database_string, (err, db) => {
this.db.on('error', function() {
console.error("Lost connection to mongodb(error), exiting".bold.red);
return process.exit(1);
}); // deal breaker for now
}

Related

Testing http.Server.close error in NodeJS

I have a code in NodeJS responsible to close an http.Server connection and I would like to test the error scenario on the http.Server.close() method.
The problem is to do that, I need to simulate the return of the close method with the err object populated, and I don't know how to do it.
Below you can find my code and I would like to test the line where we can find the code reject(err);
Note: In my integration tests, I'm starting temp HTTP servers to simulate the real scenario. So as far as I understood I need to find a real scenario where the .close method will be rejected by the default implementation.
Thanks.
this._httpServer = http.createServer((req: http.IncomingMessage, res: http.ServerResponse) => this.handler(req, res));
...
disconnect(): Promise<void> {
return new Promise((resolve, reject) => {
this._httpServer.close((err) => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
}
I found the answer.
Based on the official documentation, the unique error scenario is when we are trying to close an already closed server.
So, to make the test work, before calling my disconnect method I only need to close the httpServer (this._httpServer.close()).
Reference: https://nodejs.org/docs/latest-v12.x/api/net.html#net_server_close_callback

What is the proper way to handle connecting and closing the MongoDB Client from NodeJS (not using Mongoose!)?

export const client = new MongoClient(
process.env.ATLAS_URI,
// TODO: Figure out what this is and why it's needed to turn off deprecation warning
{
useUnifiedTopology: true,
}
);
Following this guide and all make sense...but she is just doing one 'call' and then close().
I need to keep doing repeated calls:
export const getAllProducts = async () => {
try {
await client.connect();
const cursor = await client.db("products").collection("data").find();
return await cursor.toArray();
} catch (err) {
throw new Error(err);
} finally {
await client.close();
}
};
The first call is fine. After that: Error: MongoError: Topology is closed, please connect
I honestly don't quite understand what Topology means, but evidently it's the close() that's contributing to the issue.
It doesn't make sense that I set up new MongoClient and the ATLAS_URI does have the 'database name' in there...so why I have to connect specify that again?
Anyway, the main part of my ❓ stands: Do I just keep a separate process going and not close it? Do I start back with a whole new MongoClient each time? 😕
I'll just put a brief answer here incase anyone runs into this.
The Mongodb documentation for the Node.js driver will give you simple examples that include the client.connect()and client.close() methods just to give you a runnable example of making a simple call to the database but in a real server application you are just opening the connection to the client once during start up and typically only closing when the server application is being closed.
So in short: You don't need to open and close and connection everytime you want to perform some action on your database.

Node.js: mongoose.once('open') doesn't execute callback function

I'm trying to save some json files inside my database using a custom function I've wrote. To achieve that I must connect to the database which I'm trying to do using this piece of code at the start of the function:
let url = "mongodb://localhost:27017/database";
(async () => {
const directory = await fs.promises.readdir(__dirname + '/files')
let database = await mongoose.createConnection(url, {useNewUrlParser:true, useUnifiedTopology:true});
database.on('error', error => {
throw console.log("Couldn't Connect To The Database");
});
database.once('open', function() {
//Saving the data using Schema and save();
Weirdly enough, when executing database.once('open', function()) the callback function isn't being called at all and the program just skips the whole saving part and gets right to the end of the function.
I've searched the web for a solution, and one solution suggested to use mongoose.createConnection instant of mongoose.connect.
As you can see it didn't really fixed the issue and the callback function is still not being called.
How can I fix it, and why it happens?
Thanks!
mongoose.createConnection creates a connection instance and allows you to manage multiple db connections as the documentation states. In your case using connect() should be sufficient (connect will create one default connection which is accessible under mongoose.connection).
By awaiting the connect-promise you don't actually need to listen for the open event, you can simply do:
...
await mongoose.connect(url, {useNewUrlParser:true, useUnifiedTopology:true});
mongoose.model('YourModel', YourModelSchema);
...

When is it advised to close a mongodb connection in a nodejs express application?

I m trying to make the most effective way of writing a mongodb connection setup for an express based node js app.
I have made a class that takes care of connection set up as follows:
class queryToDB {
async makeDBCall(queryHandler) {
let resultSet;
await MongoClient.connect(config.mongodburl, (err, client) => {
if(err) throw Error("Database connection cannot be established.");
resultSet = queryHandler(client.db("dbname"));
client.close();
});
return resultSet
}
};
export default new queryToDB();
After each query, I m closing the connection to the MongoClient. Is it the advised way to do it ?
Secondly, I m passing the connection to a callback as queryHandler. The queryHandler function would look something like this:
export const getCall = (id, handler) => {
return connection => {
connection.collection('some_schema').findOne({"_id": getObjectId(id)}, (err, result) => {
if(err) throw new Error(err);
handler(result);
});
}
};
I m passing the result back to the handler which in turn is passed back to the client from the server. Is this an effective way of creating connections and handling results ? So far I haven't done any kind of load testing on this but want to be sure if there is any issue with this approach of handling of queries and result to mongodb. I m also aware that I m using a couple of callbacks to achieve this, which is why I want to know more about the performance of this approach. I don't want to use Mongoose for this work. I m looking for just implementing this with MongoClient.
Any kind of feedback is appreciated. Thanks.

Keeping open a MongoDB database connection

In so many introductory examples of using MongoDB, you see code like this:
var MongoClient = require('mongodb').MongoClient;
MongoClient.connect("mongodb://localhost:port/adatabase", function(err, db)
{
/* Some operation... CRUD, etc. */
db.close();
});
If MongoDB is like any other database system, open and close operations are typically expensive time-wise.
So, my question is this: Is it OK to simply do the MongoClient.connect("... once, assign the returned db value to some module global, have various functions in the module do various database-related work (insert documents into collections, update documents, etc. etc.) when they're called by other parts of the application (and thereby re-use that db value), and then, when the application is done, only then do the close.
In other words, open and close are done once - not every time you need to go and do some database-related operation. And you keep re-using that db object that was returned during the initial open\connect, only to dispose of it at the end, with the close, when you're actually done with all your database-related work.
Obviously, since all the I/O is asynch, before the close you'd make sure that the last database operation completed before issuing the close. Seems like this should be OK, but i wanted to double-check just in case I'm missing something as I'm new to MongoDB. Thanks!
Yes, that is fine and typical behavior. start your app, connect to db, do operations against the db for a long time, maybe re-connect if the connection ever dies unexpectedly, and then just never close the connection (just rely on the automatic close that happens when your process dies).
mongodb version ^3.1.8
Initialize the connection as a promise:
const MongoClient = require('mongodb').MongoClient
const uri = 'mongodb://...'
const client = new MongoClient(uri)
const connection = client.connect() // initialized connection
And then call the connection whenever you wish you perform an action on the database:
// if I want to insert into the database...
const connect = connection
connect.then(() => {
const doc = { id: 3 }
const db = client.db('database_name')
const coll = db.collection('collection_name')
coll.insertOne(doc, (err, result) => {
if(err) throw err
})
})
The current accepted answer is correct in that you may keep the same database connection open to perform operations, however, it is missing details on how you can retry to connect if it closes. Below are two ways to automatically reconnect. It's in TypeScript, but it can easily be translated into normal Node.js if you need to.
Method 1: MongoClient Options
The most simple way to allow MongoDB to reconnect is to define a reconnectTries in an options when passing it into MongoClient. Any time a CRUD operation times out, it will use the parameters passed into MongoClient to decide how to retry (reconnect). Setting the option to Number.MAX_VALUE essentially makes it so that it retries forever until it's able to complete the operation. You can check out the driver source code if you want to see what errors will be retried.
class MongoDB {
private db: Db;
constructor() {
this.connectToMongoDB();
}
async connectToMongoDB() {
const options: MongoClientOptions = {
reconnectInterval: 1000,
reconnectTries: Number.MAX_VALUE
};
try {
const client = new MongoClient('uri-goes-here', options);
await client.connect();
this.db = client.db('dbname');
} catch (err) {
console.error(err, 'MongoDB connection failed.');
}
}
async insert(doc: any) {
if (this.db) {
try {
await this.db.collection('collection').insertOne(doc);
} catch (err) {
console.error(err, 'Something went wrong.');
}
}
}
}
Method 2: Try-catch Retry
If you want more granular support on trying to reconnect, you can use a try-catch with a while loop. For example, you may want to log an error when it has to reconnect or you want to do different things based on the type of error. This will also allow you to retry depending on more conditions than just the standard ones included with the driver. The insert method can be changed to the following:
async insert(doc: any) {
if (this.db) {
let isInserted = false;
while (isInserted === false) {
try {
await this.db.collection('collection').insertOne(doc);
isInserted = true;
} catch (err) {
// Add custom error handling if desired
console.error(err, 'Attempting to retry insert.');
try {
await this.connectToMongoDB();
} catch {
// Do something if this fails as well
}
}
}
}
}

Resources