UnhandledPromiseRejectionWarning in Express App - javascript

I'm currently learning Javascript/Node.js/MEAN stack and I'm following an Express tutorial.
I get the following error:
(node:11524) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): TypeError: Cannot read property 'close' of undefined
(node:11524) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
When I hit this route in my browser.
function router(nav) {
adminRouter.route('/')
.get((req, res) => {
const url = 'mongodb://localhost:27017';
const dbName = 'libraryApp';
(async function mongo() {
let client;
try {
client = await mongoClient.connect(url);
debug('Connected correctly to server');
const db = client.db(dbName);
const response = await db.collection('books').insertMany(books);
res.json(response);
} catch (err) {
debug(err.stack);
}
client.close();
}());
});
return adminRouter;
}
Could someone point out the issue and explain what the issue is please.

If this line rejects:
client = await mongoClient.connect(url);
Then, you go to your catch block and after that catch block, you call client.close(). But, client is undefined so client.close() will throw and you are not inside any sort of try/catch at that point. Since you're inside an async function, that throw will turn into a rejected promise which you have no .catch() to handle. Thus, you end up with an unhandled promise rejection.
You should be able to fix it like this:
function router(nav) {
adminRouter.route('/').get(async (req, res) => {
const url = 'mongodb://localhost:27017';
const dbName = 'libraryApp';
let client;
try {
client = await mongoClient.connect(url);
debug('Connected correctly to server');
const db = client.db(dbName);
const response = await db.collection('books').insertMany(books);
res.json(response);
} catch (err) {
debug(err.stack);
// make sure and send some response on errors
res.sendStatus(500);
}
if (client) {
client.close();
}
});
return adminRouter;
}
This makes several changes:
Add if (client) before calling client.close() to protect it from the case where `client never got set.
Make the whole .get() callback be async rather then using an IIFE (not required, just seems cleaner to me)
Send an error status and response in your catch statement so you are always sending an http response of some kind, even in error situations.
If you really wanted to be fail-safe, you could just add another try/catch:
function router(nav) {
adminRouter.route('/').get(async (req, res) => {
const url = 'mongodb://localhost:27017';
const dbName = 'libraryApp';
let client;
try {
client = await mongoClient.connect(url);
debug('Connected correctly to server');
const db = client.db(dbName);
const response = await db.collection('books').insertMany(books);
res.json(response);
} catch (err) {
debug(err.stack);
// make sure and send some response on errors
res.sendStatus(500);
}
try {
if (client) {
client.close();
}
} catch(e) {
console.log("unable to close database connection");
}
});
return adminRouter;
}

Related

POST request... UnhandledPromiseRejectionWarning: Unhandled promise rejection

When making a post request I get this error:
UnhandledPromiseRejectionWarning: Unhandled promise rejection.
If somebody could explain why this is happening I would appreciate it :) Thanks
UPDATE
So, the this code POSTs successfully. But, when I uncomment the validation code I get that same error...
router.post("/", async (req, res) => {
//let client = validate(req.body);
//if (client.error) {
//res.status(400).json(result.error);
//return;
//}
let client = new Client(req.body);
try {
let savedClient = await client.save();
res.location(`/${savedClient._id}`).status(201).json(savedClient);
} catch (error) {
res.status(500).json(savedClient.error);
}
});
I don't see where you initialise savedClient and I think your error lies in your catch. You're referencing an object (savedClient) which doesn't appear to be in scope.
Try this:
router.post("/", async (req, res) => {
let client = new Client(req.body);
try {
let savedClient = await client.save();
res.location(`/${savedClient._id}`).status(201).json(savedClient);
} catch (error) {
console.log(error)
res.status(500).json(error);
}
});
maybe
let savedClient = await client.save();
I think it should be
...
} catch (error) {
res.status(500).json(savedClient.error);
}
because the promise's rejection error should be catch by the try/catch statement.

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))
})

nodejs promises giving Unhandled Promise Rejection which seems to be handled properly

The problem is that i am getting UNhandledPromiseRejection error eveen though i think i have handled all the cases. The code flows from profileRoutes to Controller to Utils where the error comes first.
Inside the profileRoutes.js
router.get('/:username', async (r, s) => {
try{
let profileData = await getProfileData(r.params.username);
s.json({ success: true, payload: profileData });
}catch(err){
console.log('ending request processing by responding a error');
s.status(500).json({ success: false, message: 'err[0].message' });
}
});
Inside the controllers/index.js
const fetchQueue = [getUserRepos];
async function getProfileData(username) {
let profileData = {};
try{
let results = await Promise.all(fetchQueue.map(item => item(username)));
for (let i = 0; i < results.length; i++) {
profileData[getKeys[i]] = results[i];
}
return profileData;
}catch(err){
console.log('error log in controller/index getProfileData function');
throw err;
}
}
const getUserRepos = async (username) => {
try {
// const res = await utils.gqlSender(username, 'userRepos', { createdAt });
const res = await utils.gqlSender(username, 'userReposData');
return res.user.repositories;
} catch (err) {
console.log('error log in controller/index getUserRepos function');
throw err;
}
};
Inside the utils/index.js
const gqlSender = async (username, type, opt = {}) => {
axios.post('', {
query: gqlQuery(username, type, opt) // generates a needed graphQL query
}).then(res => {
if(res.data.errors) { // this is where the error is recieved and so i reject promise.
console.log('bef###re');
return Promise.reject (res.data.errors);
}
console.log('###',res.data);
return res.data;
}).catch(err => {
console.log('error in making axios request inside utils/index gqlSender function');
throw err;
// return Promise.reject(err);
});
The stack trace on making get request to /:username is-
error log in controller/index getUserRepos function
error log in controller/index getProfileData function
ending request processing by responding a error
bef###re
error in making axios request inside utils/index gqlSender function
(node:11260) UnhandledPromiseRejectionWarning: [object Array]
(node:11260) UnhandledPromiseRejectionWarning: Unhandled promise rejection. 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: 2)
(node:11260) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
I dont think i am missing any Promise Rejection.
Any help is appreciated. Thanks.
i have referred these answers previously -
What's the difference between returning value or Promise.resolve from then()
Do I need to return after early resolve/reject?
Your gqlSender function is not returning the promise that will get rejected, so it is not handled anywhere. You should write either
const gqlSender = (username, type, opt = {}) => {
return axios.post('', {
// ^^^^^^
query: gqlQuery(username, type, opt) // generates a needed graphQL query
}).then(res => {
if (res.data.errors) {
console.log('error in making axios request inside utils/index gqlSender function');
throw res.data.errors;
} else {
console.log('###',res.data);
return res.data;
}
});
};
or
const gqlSender = async (username, type, opt = {}) => {
// ^^^^^
const res = await axios.post('', {
query: gqlQuery(username, type, opt) // generates a needed graphQL query
});
if (res.data.errors) {
console.log('error in making axios request inside utils/index gqlSender function');
throw res.data.errors;
} else {
console.log('###',res.data);
return res.data;
}
}

Global connection already exists despite sql.close()

I created query function to connect to mssql database like below:
query(query) {
return sql.connect(config.connection.sqlserver).then(() => sql.query(query)).then((result) => {
sql.close();
return result.recordset;
}).catch((err) => console.log(err));
},
When I'm starting nodejs server everything works fine. When I'm doing refresh many times I'm getting result from database e.g.
But when I'm sending request from client side to server side I'm getting an error like below:
(node:19724) UnhandledPromiseRejectionWarning: Error: Global connection already
exists. Call sql.close() first.
at Object.connect (C:\repos\mtg-app\node_modules\mssql\lib\base.js:1723:31)
at query (C:\repos\mtg-app\server\query.js:6:16)
at asyncCall (C:\repos\mtg-app\server\routes\index.js:19:11)
at router.get (C:\repos\mtg-app\server\routes\index.js:29:3)
at Layer.handle [as handle_request] (C:\repos\mtg-app\node_modules\express\l
ib\router\layer.js:95:5)
at next (C:\repos\mtg-app\node_modules\express\lib\router\route.js:137:13)
at Route.dispatch (C:\repos\mtg-app\node_modules\express\lib\router\route.js:112:3)
at Layer.handle [as handle_request] (C:\repos\mtg-app\node_modules\express\lib\router\layer.js:95:5)
at C:\repos\mtg-app\node_modules\express\lib\router\index.js:281:22
at Function.process_params (C:\repos\mtg-app\node_modules\express\lib\router\index.js:335:12)
(node:19724) UnhandledPromiseRejectionWarning: Unhandled promise rejection. 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)
(node:19724) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
I don't understand first of all why it's working only on server and secondly why it's not working despite sql.close() method?
Please explain me this issue very well.
In regards to my comment, something along these lines.
async function query(q) {
return new Promise((resolve, reject) => {
new sql.ConnectionPool({/* Config */}).connect().then((pool) => {
return pool.request().query(q)
}).then((result) => {
// Resolve result
resolve(result);
// Close sql.close();
sql.close();
}).catch((error) => {
// Reject error
reject(error);
// Close sql.close();
sql.close();
});
});
}
query(`SELECT * FROM`)
.then((response) => {
// Success
console.log(response);
})
.catch((error) => {
// Error
console.log(error);
})
.finally(() => {
// Clean-up
});
Or another way, with mysqlijs all from the documentation here.
const mysql = require('mysql');
const pool = mysql.createPool({/* Config */});
pool.getConnection(function(error, connection) {
if (error) {
throw error;
}
// Use the connection
connection.query(`SELECT * FROM`, function (error, results, fields) {
// When done with the connection, release it.
connection.release();
// Handle error after the release.
if (error) throw error;
// Don't use the connection here, it has been returned to the pool.
});
});
pool.on('acquire', function (connection) {
console.log('Connection %d acquired', connection.threadId);
});
pool.on('enqueue', function () {
console.log('Waiting for available connection slot');
});
pool.on('release', function (connection) {
console.log('Connection %d released', connection.threadId);
});
pool.end(function (err) {
// Ending all connection the the MySQL pool.
});
I did it in the simpler way than below. But thanks Raymond because I realized that I should use ConnectionPool.
My query module:
/* eslint-disable import/no-unresolved */
const mssql = require('mssql');
const database = require('./config/connection');
const pool = new mssql.ConnectionPool(database.config).connect();
async function query(sql) {
try {
const connection = await pool;
const result = await connection.query(sql);
return result.recordset;
} catch (err) {
console.log('SQL error', err);
}
return [];
}
module.exports.query = query;
Now when I want to use it e.g. in router module:
router.get('/get-users', (req, res, next) => {
const usersStandings = [];
sql.query('select u.name, s.points from dbo.[user] u join dbo.standings s on s.id = u.standings_id join dbo.category c on c.id = s.category_id where c.type = \'Tournament\' order by points desc').then((rows) => {
rows.forEach((element) => {
usersStandings.push(element);
});
res.send(usersStandings);
});
});
Now I don't have problems with Global connection etc.

Unhandled promise rejection. This error originated either by throwing inside of an async function - NodeJS

I'm very new with NodeJS. I'm trying to create a simple server that has a connection to my mongoDB Atlas database but when I run my server I get this error:
UnhandledPromiseRejectionWarning: Unhandled promise rejection. 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) (node:8825) [DEP0018]
DeprecationWarning: Unhandled promise rejections are deprecated. In
the future, promise rejections that are not handled will terminate the
Node.js process with a non-zero exit code.
Seems to be a common problem based on what I googled, I added the try/catch but it still isn't working.
'use strict';
//const AWS = require('aws-sdk');
const express = require('express');
const mongoose = require('mongoose');
const uuidv4 = require('uuid/v4');
//exports.handler = (event, context, callback) => {
mongoose.connect(
'mongodb+srv://xxxx:xxxx#cluster0-us8bq.mongodb.net/test?retryWrites=true',
{
useNewUrlParser: true
}
),
() => {
try {
//something
} catch (error) {
console.error(error);
}
};
const connection = mongoose.connection;
connection.once('open', () => {
console.log('🖥 Connection to DB was succesful');
});
const app = express();
app.listen({ port: 4800 }, () =>
console.log(`🚀 Server ready at http://localhost:4800`)
);
Mongoose connect returns promise, and most probably there is an error when it attempts to connect: I would suggest using the async function, to handle DB connection. Here is what I use currently.
const config = require('config').db; // Your DB configuration
const combineDbURI = () => {
return `${config.base}${config.host}:${config.port}/${config.name}`;
};
// Connecting to the database
const connect = async function () {
const uri = combineDbURI(); // Will return DB URI
console.log(`Connecting to DB - uri: ${uri}`);
return mongoose.connect(uri, {useNewUrlParser: true});
};
And then call it within an async function using await:
(async () => {
try {
const connected = await connect();
} catch(e) {
console.log('Error happend while connecting to the DB: ', e.message)
}
})();
Or you can call without await using promise API:
connect().then(() => {
console.log('handle success here');
}).catch((e) => {
console.log('handle error here: ', e.message)
})
Besides, using try catch when using callbacks does not make sense, when you don't have promises, you should use error callbacks to catch errors.
So to answer your question (as others mentioned in the comments):
As connect function returns a promise, you should use catch callback to catch the promise rejection. Otherwise, it will throw Unhandled Promise Rejection.
I hope this will help.
UnhandledPromiseRejectionWarning means that promises should have .catch() like
mongoose.connect(...).catch(err => console.log(err))

Categories

Resources