NodeJS: Unhandled Promise Rejection at the end of a method - node.js

At the moment, I'm working on a RESTful-API with express and mongoose and I now have a problem.
First, my method:
public create() : Promise<UserDocument> {
return new Promise((user) => {
User.exists(this.username).then((exists) => {
if (exists) {
throw Errors.mongoose.user_already_exists;
} else {
UserModel.create(this.toIUser()).then((result) => {
user(result);
}).catch(() => {
throw Errors.mongoose.user_create
});
}
}).catch((error) => {
throw error;
})
});
}
I get a unhandled promise rejections when I execute this method. This happens even if I handle the error when I execute the method like this:
User.fromIUser(user).create().then(() => {
return response.status(200).send({
message: "Created",
user
});
}).catch((error) => {
return response.status(500).send({
message: error
});
});
Full stacktrace:
(node:23992) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): User already exists
(node:23992) 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.
How i can avoid this situation?
Thanks for your help,
felixoi

I found the solution! Just use "resolve, request" for creating a promise.
Here is now my method:
public create() : Promise<any> {
return new Promise((resolve, reject) => {
User.exists(this.username).then((exists) => {
if (exists) {
reject( Errors.mongoose.user_already_exists);
} else {
UserModel.create(this.toIUser()).then((result) => {
resolve(result);
}).catch(() => {
reject(Errors.mongoose.user_create);
});
}
}).catch((error) => {
reject(error);
})
})
}
If you call the method now you can use the catch() method and everything works! Call it like this:
User.fromIUser(user).create().then((user) => {
return response.status(200).send({
message: "Created",
user
});
}).catch((error) => {
return response.status(500).send({
message: error
})
})

Related

Mongo FindOne return results in error parameter

I am doing a login system in NodeJS with a Mongo database, so I try to look in my collection if the user exist. For my tests, I have a user registered, and I'm trying to find him.
My problem is that findOne method return the full user' data as I wish, but in the error parameter, not in the results parameter, and I have no logs to understand why...
Did somebody know why ?
Here is my code:
app.post('/login', (req, res) =>{
console.log(req.body.identifier);
console.log(req.body.password);
client.connect().then(() => {
let newUser = {identifier : req.body.identifier}
res.redirect(req.body.locator)
return client.db(`${process.env.MONGODB}`).collection(`${process.env.MONGOCOLLECTION}`).findOne(newUser).then((err, res) => {
if (err){
console.log("ERROR: "+err.role)
throw err;
}else if(res){
console.log("user found");
console.log(res.role)
}
})
}).catch( e => { console.error(e) }).then(() => {
console.log("--------------------------------");
})
})
And this is what I got :
mail#mail.com
azer
ERROR: USER
{
_id: 6087d850ad9f6f2e0ce97045,
identifier: 'mail#mail.com',
password: '7657d9148a5720dcf4eb4b8bc998498e5d701ce7beb302f398c3d5c0dbd0f857f824b7bfaa45c2a8aba4f85c4ab8b12c99bfb28328e72a89afe11326dc1d3a38349c1c36790a24c910528ada34529e6736ae45f0e5d87ce6b109207e21169bc9b4056fff',
role: 'USER',
sign_up: 2021-04-27T09:24:32.616Z,
name: 'Test',
firstName: 'Test'
}
--------------------------------
This occurs because your code mixes Promises and callbacks. In particular:
findOne(newUser).then((err, res) => {
// (this does not work because .then() takes a function with 1 argument)
})
Promises have a built-in error handling mechanism where a resolver can either resolve or reject the promise, triggering the .then() (success) or the .catch() (failure) branch.
See the note at the top of the relevant manual page. Also, MongoDB provides a short guide on Promises vs. callbacks - pay attention especially to this snippet:
collection
.updateOne({ name: "Mount McKinley" }, { $set: { meters: 6190 } })
.then(
res => console.log(`Updated ${res.result.n} documents`),
err => console.error(`Something went wrong: ${err}`),
);
Note how .then() accepts two functions above - these are separate callbacks for the "success" case and the "error" case.
Read - Promises and Callbacks
Here you're using promise - then will give the result and catch will give the error.
client.db(`${process.env.MONGODB}`)
.collection(`${process.env.MONGOCOLLECTION}`)
.findOne(newUser)
.then( res => { ... }) // result
.catch(err => { ... }) // error
Callback style
client.db(`${process.env.MONGODB}`)
.collection(`${process.env.MONGOCOLLECTION}`)
.findOne(newUser, (err, res) => { ... })
The mongodb was returning the data, and the data was taken as first argument in the then block named as err, try changing to this:
app.post("/login", (req, res) => {
console.log(req.body.identifier);
console.log(req.body.password);
client
.connect()
.then(() => {
let newUser = { identifier: req.body.identifier };
res.redirect(req.body.locator);
return client
.db(`${process.env.MONGODB}`)
.collection(`${process.env.MONGOCOLLECTION}`)
.findOne(newUser, (err, res) => {
if (err) {
console.log("ERROR: " + err.role);
throw err;
} else if (res) {
console.log("user found");
console.log(res.role);
}
});
})
.catch((e) => {
console.error(e);
})
.then(() => {
console.log("--------------------------------");
});
});

Async/await mvc express problems handling errors with .catch()

I'm trying to handle errors using express middleware, with these lines I have the following errors
user.js controller
app.post('/create', async (req, res, next) => {
const data = await User.create(req.body)
.catch((err) => next(err));
res.status(201).json({ ok: true, ...data });
});
user.js model
UserSchema.statics.create = async function createUser(data) {
delete data.role;
const user = await new this(data).save();
return { token: user.newToken(), user };
};
app.js
app.use((err, req, res, next) => {
res.status(err.code || 400);
res.json({ ok: false, err: err.message });
});
Errors
(node:3304) UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
...
(node:3304) 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(). To terminate the node process on unhandled promise rejection, use the CLI flag --unhandled-rejections=strict (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 9)
(node:3304) [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.
After to proof with try/catch in the user.js controller i don't have any error, but in the express documentation use try/catch is not recommended.
app.post('/create', async (req, res, next) => {
try {
const data = await User.create(req.body)
res.status(201).json({ ok: true, ...data });
} catch (err) {
next(err);
}
});
Any ideas?
You either use await or then/catch:
app.post('/create', async (req, res, next) => {
User.create(req.body)
.then(data => {
res.status(201).json({ ok: true, ...data });
})
.catch((err) => next(err));
});

How to catch mongoose errors when updating a document

In my Node.js backend, I have an endpoint where user profile info is updated. I want to send user an error response if the new email is already in the database. However, though I have set up a try-catch in place, I still can't catch the error. Instead, the server just crashes with the following mongoose error message. I receive an error response on the front end, but after a very long time from when the error happened. All helpful advice is highly appreciated.
(node:11864) UnhandledPromiseRejectionWarning: MongoError: E11000
duplicate key error collection: testing.users index: email_1 dup key:
{ : "test#gmail.com" } ... (node:11864)
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(). To terminate the node process on unhandled promise
rejection, use the CLI flag --unhandled-rejections=strict (see
https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode).
(rejection id: 1)
UpdateProfile in UserController.js
updateProfile: (id,fname,lname,email,mobile,address,next) => {
let args = {fname,lname,email,mobile,address}
try{
User.findOneAndUpdate({ "_id": id },
{ "$set": Utils.removeNullsFromObject(args)},
{new: true, useFindAndModify: false}).then(function(updatedUser, err){
if(err) {
console.log(err);
next(err,null)
} else {
next(null,updatedUser)
}
);
}
catch(err){
console.log(err);
next(err,null)
}
}
Try...catch will work with async/await and not with promise...then. promise...then has special block called catch which can be used as,
updateProfile: (id,fname,lname,email,mobile,address,next) => {
let args = {fname,lname,email,mobile,address}
User.findOneAndUpdate({ "_id": id },
{ "$set": Utils.removeNullsFromObject(args)},
{
new: true, useFindAndModify: false
}).then(updatedUser => {
next(null,updatedUser)
).catch(err =>{
console.log(err);
next(err,null)
})
}
and if you want to use async/await, then,
updateProfile: async (id,fname,lname,email,mobile,address,next) => {
let args = {fname,lname,email,mobile,address}
try{
const updatedUser = await User.findOneAndUpdate({ "_id": id },
{ "$set": Utils.removeNullsFromObject(args)},
{
new: true, useFindAndModify: false
})
next(null,updatedUser)
} catch(err) {
console.log(err);
next(err,null)
})
}
For more details, you can refer https://javascript.info/promise-error-handling

Unhandled promise rejection in express route

I am getting this error:
UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Can't set headers after they are sent.
from this portion of my express route route:
router.post('/', jsonParser, (req, res) => {
// checking that given id is valid
let { id } = req.body;
User.findById({ id })
.count()
.then(count => {
if (count < 1) {
return Promise.reject({
code: 422,
reason: 'Validation Error',
message: 'Family must be created by a user',
location: 'id'
})
}
return resolve();
})
.catch(err => {
return res.status(err.code).json({code: err.code, message: err.message, reason: err.reason, location: err.location })
});
...
I'm not stellar at promises. Can someone see what I am doing incorrectly here?
resolve is undefined, you could use return count instead, to pass data to the next promise:
User.findById({ id })
.count()
.then(count => {
if (count < 1) {
throw new Error('Family must be created by a user')
}
// success
return count;
})
.then( res => { // res will be count if the promise not rejected
res.json({ success: true, count: res })
})
.catch(err => {
return res.status(422).json({
code: 422,
reason: 'Validation Error',
message: 'Family must be created by a user',
location: 'id'
})
});
.catch(err => { return res.status(err.code).json({code: err.code, message: err.message, reason: err.reason, location: err.location }) });
Here lies the issue. when a response is sent you can not send another response. So when you do
res.status(), express sends the response with status code. and .json() will give exception.
try setting status like this.
res.statusCode = code; and then
res.json()

Unhandled promise rejection - Error: Can't set headers after they are sent

I am new to node, and I have a simple situation, where I am posting to an endpoint on a node/express app. The issue is that I get:
POST /api/v2/user 500 25.378 ms - 54
(node:19024) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 2): Error: Can't set headers after they are sent.
(node:19024) 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.
The relevant code that I have which is generating this is:
router.post('/', (req, res, next) => {
return authHelpers.createUser(req, res)
.then((user) => {
return localAuth.encodeToken(user[0]);
})
.then((token) => {
res.status(201).json({
status: 'success',
message: 'User Created',
token: token
});
})
.catch((err) => {
res.status(500).json({
status: 'error'
});
});
});
and then:
function createUser(req, res) {
return handleErrors(req)
.then(() => {
const salt = bcrypt.genSaltSync();
const hash = bcrypt.hashSync(req.body.password, salt);
return knex('users')
.insert({
email: req.body.email,
first_name: req.body.first_name,
last_name: req.body.last_name,
username: req.body.username,
password: hash
})
.returning('*');
})
.catch((err) => {
res.status(410).json({
status: err.message
});
});
}
function handleErrors(req) {
return new Promise((resolve, reject) => {
if (req.body.username.length < 6) {
reject({
message: 'Username must be longer than 6 characters'
});
} else if (req.body.password.length < 6) {
reject({
message: 'Password must be longer than 6 characters'
});
} else {
resolve();
}
});
}
I do know that if I remove the res.status(500).json({status: 'error'}); specifically, then the error goes away, but I am not sure if that is proper.
Any clue to what exactly is my error and how to fix?
You are trying to send response twice. First when catching the error
res.status(410).json({
status: err.message
});
And then after catch, promise chain continues the normal route until:
return localAuth.encodeToken(user[0]);
Which fails, because user is undefined and throws an exception.. so error handler is called and you are trying to send response again, but it fails because it has already been sent once
res.status(500).json({
status: 'error'
});
console log which error was thrown in the last part, I'm pretty sure it is something like
TypeError: Cannot read property '0' of undefined

Resources