How to handle error properly with nested promises in different modules - javascript

I'm new to Node JS, and I'm struggling to handle error properly when using promises.
Here is what I currently have:
I have a module call db.js with an init function used to set up and start the db connection:
function initDb(){
return new Promise((resolve, reject) => {
if(database){
console.warn("Trying to init DB again !")
}else{
client.connect(config.db.url, {useNewUrlParser: true, useUnifiedTopology: true})
.then((client) => {
console.log("DB initialized - connected to database: " + config.db.name)
database = client.db(config.db.name)
})
.catch((err) => {
reject(err)
})
}
resolve(database)
})}
This function will return a promise, and it will be called into the index.js:
initDb()
.then(() => {
app.listen(8081, function () {
console.log('App is running on port 8081')
})
})
.catch((error) => {
console.log(error)
})
As you can see I have two catch. One in the db module and the other one in the index.js
It seams weird to catch error in two places... What is the good pattern to use to handle error in this case ?

You'll want to avoid the Promise constructor antipattern. Also don't store the database itself in a variable, store the promise for it instead.
let databasePromise;
function initDb() {
if (databasePromise) {
console.debug("DB already initialised");
return databasePromise;
}
databasePromise = client.connect(config.db.url, {useNewUrlParser: true, useUnifiedTopology: true})
.then((client) => {
console.log("DB initialized - connected to database: " + config.db.name)
return client.db(config.db.name)
});
return databasePromise;
}

If you don't write catch in the initDb function in db.js module, you can catch that error in the calling function, so it's okay if you don't write .catch((err) => { reject(err)}) in db.js module, the error will go to calling function in index.js and you can directly handle it there.
It is not weird to catch the error at both the place, in fact in coming versions of node this will be a recommended practice to write catch(err) for handling errors at all promises.

Related

How to connect to a MongoDB asynchronously?

I have a MongoDB database. I'm using Javascript and Node.js (using Mangoose) to access this database. I need to return an array with the name of all the collections in the database. So I use a code that follows.
let connection = mongoose.createConnection(process.env.MONGODB_URI + "/" + dbname);
// Wait 10 seconds for Mongoose to establish the connection.
await new Promise(r => setTimeout(r, 10000));
return connection.db.collections()
.then(stations=>stations.map(stations=>stations.collectionName))
.catch(reason => {
console.error("Error : "+reason);
return null;
});
The code above is working correctly. Now, I'm trying to do the same process asynchronously. For this, I am using the following code.
async function station_list_all(dbname){
return new Promise((resolve,reject) => {
try {
let connection = mongoose.createConnection(process.env.MONGODB_URI + "/" + dbname);
resolve(connection);
}catch(err){
reject(new Error("DB not found!"));
}
})
.then(connection => connection.db)
.then(db => db.collections())
.then(stations=>stations.map(station=>station.collectionName))
.catch(err => {
console.error("Error : "+err);
return null;
});
}
Unfortunately, instead of the collection names, I get the message: Error : TypeError: Cannot read property 'collections' of undefined.
I think db is returning undefined, but... Shouldn't the code wait until db has a valid value?
Try with async await:
try {
await mongoose.connect('mongo url with db name');
// other process
} catch (error) {
handleError(error);
}
Or you can connect using callback:
try {
mongoose.connect('mongo url with db name').then(()=>{
// other process
});
} catch (error) {
handleError(error);
}
In Same way you can try with promise also. There is no need use settimeout.
For More Ref Please visit: Mongoose Connections

NodeJS UnhandledPromise warning when connecting to MongoDB

I am trying to connect to my MongoDB instance using nodejs. I expose the endpoint /mongo which is supposed to trigger the connection and creation of a document in the mongo db, as follows:
app.get('/mongo', (req, res) => {
try{
invoke();
} catch(err){
console.log(err);
}
res.send('all good.');
});
async function invoke() {
client.connect(err => {
const collection = client.db("CodigoInitiative").collection("Registered");
//create document to be inserted
const pizzaDocument = {
name: "Pizza",
shape: "round",
toppings: [ "Pepperoni", "mozzarella" ],
};
// perform actions on the collection object
const result = collection.insertOne(pizzaDocument);
console.log(result.insertedCount);
//close the database connection
client.close();
});
}
When I hit the endpoint though, it returns with the following error:
(node:15052) UnhandledPromiseRejectionWarning: MongoError: topology was destroyed. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
I'm confused because the method invocation was wrapped around a try/catch block, even though the error log claims it wasn't. Where did I go wrong here?
There could be connection error in your environment. And the error was a rejected promise, you cannot catch it via the try / catch block because the error was generated on asynchronous call stack.
an async function should alway return a promise:
async function invoke () {
return new Promise((resolve, reject) => {
client.connect(err => {
if (err) return reject(err)
...
})
})
}
the returned promise should be handled with .catch:
app.get('/mongo', (req, res) => {
invoke().then(() => res.send('all good'))
.catch(err => console.log('invoke error:', err))
})

node.js - get value of promise

let { errors } = otherValdations(data);
withDB(async (db) => {
return Promise.all([
..code...
]).then(() => {
return {
errors,
isValid: isEmpty(errors),
}
})
}, res).then((result) => {
console.log(result);
})
How can I get 'result' variable to be the value of the object returned in promise.all? This is the code for withDB function:
const withDB = async (operations, res) => {
try {
const client = await MongoClient.connect('mongodb://localhost:27017', { useNewUrlParser: true });
const db = client.db('app');
await operations(db);
client.close();
} catch (error) {
res.status(500).json({ message: 'Error connecting to db', error});
}
};
You need to modify withDB() so that it returns the value you want:
const withDB = async (operations, res) => {
try {
const client = await MongoClient.connect('mongodb://localhost:27017', { useNewUrlParser: true });
const db = client.db('app');
let result = await operations(db);
client.close();
return result;
} catch (error) {
res.status(500).json({ message: 'Error connecting to db', error});
throw error;
}
}
In your catch() handler, you also need to do something so that your calling code can distinguish the error path where you've already sent an error response from the case where you're resolved with the value. I don't know exactly how you want that to work, but I put in a throw error so that it will reject the returned promise and the caller can see that.
I notice from your error handling that you are assuming all possible errors are causing by an error connecting to the DB. That is not the case here. If operations(db) rejects, that will also hit your catch.
Promise.all returns an array with the results. So you either have to loop over the results or access them directly by supplying an index.

res.redirect causes error in .catch statement

I am using Node.js and have a database call being performed with a promise. I setup a '.then/.catch' to handle the result of the promise. That part all seems to work without any issue. After I do some processing of the data returned from the database I am attempting to 'redirect' to another route within the '.then' statement. This re-direction seems to be causing a problem with the '.catch' statement. I have no error issues if I remove the 'res.redirect'...is there a workaround? Any suggestions appreciated.
code:
const db = require('./routes/queries');
//**Query PostGres database...
db.getMembers()
.then(function(value) {
console.log('Async success!', value);
//do some processing on the data returned from the promise call here...
//if I insert a redirect here...such as "res.redirect('/_members');"...
//I get a "Caught an error! ReferenceError: res is not defined" message...?
//evidently the redirect fires the '.catch' statement below...why?
})
.catch(function(err) {
console.log('Caught an error!', err);
});
I recommend providing a reusable promise in case you need to find your members list elsewhere in your Express app
const db = require('./routes/queries');
const findMembers = () => {
return db.getMembers()
.then(members => {
return members
})
.catch(err => {
console.log('Caught an error!', err);
});
}
//inside your express app
app.get("/some_route", (req, res) => {
return findMembers()
.then(members => {
//do something with your members list
res.redirect("/redirect_route")
})
})

Properly terminate NodeJS Async Await

I wrote the following code to connect to MongoDB wrapped in a Promise, but as soon as it is connected, my NodeJS code is still running / not exit properly. May I know what is the proper way to return or terminate it?
function connectDB() {
return new Promise (resolve => {
MongoClient.connect(mongoUrl, function( err, db ) {
_db = db;
resolve(db);
});
})
}
(async function crawl() {
const db = await connectDB();
console.log('You are connected');
})()
This isn't really related to async/await, it also happens without. As #Torazaburo stated in the comments, the db driver maintains an open connection to the database, and this open socket (along with its eventhandlers) prevents node from quitting. Closing the connection helps:
function connectDB() {
return new Promise (resolve => {
MongoClient.connect(mongoUrl, function(err, db) {
if (err) reject(err);
else resolve(db);
});
});
}
(async function crawl() {
const db = await connectDB();
console.log('You are connected');
db.close();
console.log('You are no longer connected. Bye!');
})()
A promise have 2 paramaters: resolve and reject.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
You need to change your promise declaration:
return new Promise ((resolve, reject) => {
// ...
}
After that, add return statement to your promise:
return resolve(db)
Also, i suggest you to add an error log to your promise:
if (err) return reject(err)
And catch this error.
Good luck!

Categories

Resources