Express email error handling, for nested middleware - javascript

I have an express route for handling password resets, and with that i have a route, where i first find the user, and have some error handling with that, but now i want aditional error handling inside a nested function, and I'm not sure what pattern to use
function triggerPasswordResetEmailSend(req, res, next) {
var email = req.body.email;
if (!email) return res.status(422).json({error: "Please provide an email."});
UserRepositoryClass.findUserByEmail(email).then(user =>{
if(!user) return res.status(422).json({message: "User not found"})
sendPasswordReset(user);
return res.status(200).json({user: user});
}).catch(err =>{
return res.status(500).json({error: err})
});
}
Inside this function i do some initial error handling. The issue now is that the sendPasswordReset function can also throw errors, but there are not caught by the .catch() function, so I'm looking for something to handle this function.
I have tried passing the req and res objects into the function, but that does not seem like a good solution. I could do some try catch or maybe return a promise. But i want to ensure, that i follow the same pattern and best practises as i have already tried to do.
Here is the code snippet from my mail function:
module.exports = (user) => {
const userResetToken = generatePasswordToken();
UserRepositoryClass.setPasswordResetToken(user.id, userResetToken);
const passwordResetUrl = PASSWORD_RESET_URL(user._id, userResetToken);
return sendMail(options(user.email, passwordResetUrl));
}

You can use promise instead of function.
module.exports.sendPasswordReset = user = new Promise((resolve, reject) => {
const userResetToken = generatePasswordToken();
UserRepositoryClass.setPasswordResetToken(user.id, userResetToken);
const passwordResetUrl = PASSWORD_RESET_URL(user._id, userResetToken);
sendMail(options(user.email, passwordResetUrl))
.then(response => {
resolve(response, null); // we can get result as (data,error) here error is null
})
.catch(err => {
reject(null, err); // here response is null
});
});
You can use sendPasswordReset Promise like this:
sendPasswordReset(user).then((res, err) => {
// here you can get your res as well as err
if (err) throw new Error("Error while sending an email");
console.log("response", res);
});

Related

Javascript: using catch block but not to handle an error

I'm in a situation where I have to use a catch block to execute some code but I don't want to consider it an error.
Basically, I want to update/create a user based on whether the user is already registered or not respectively. The admin sdk let me create a user, and if the user already exists it throws an error. So if I'm in the catch block I know that the user already exists and I want to update it.
function addClient(client) {
return new Promise((resolve, reject) => {
admin.auth().createUser({
uid: client.id,
email: client.email,
emailVerified: true,
password: client.password,
}).then(record => {
resolve(record);
return null;
}).catch(
// the user already exist, I update it
admin.auth().updateUser(client.id, {
email: client.email
}).then(record => {
resolve(record);
return null;
}).catch(
err => {
reject(err);
}
)
);
});
}
The problem is that when I call the function with an existing user, it is updated correctly but the HTTP response is an internal server error (I guess because it enters the catch block and it considers this as an error). The same is if I send a new user: it is created correctly but the HTTP response code is a 500.
There is a way to avoid this behaviour?
This is the main function that calls the previous one for each user received and it's responsible for sending the HTTP response:
exports.addClients = functions.https.onRequest((req, res) => {
// fetch recevied list from payload
var receivedClients = req.body.clients;
var promises = [];
receivedClients.forEach(client => {
promises.push(addClient(client));
})
Promise.all(promises)
.then(() => {
res.sendStatus(200);
return null;
})
.catch(err => {
res.status(500).send(err);
});
});
I guess that what I want to achieve is to have all the promises resolving.
You need to pass a callback to .catch, not a promise. Also avoid the Promise constructor antipattern!
function addClient(client) {
return admin.auth().createUser({
uid: client.id,
email: client.email,
emailVerified: true,
password: client.password,
}).catch(err => {
// ^^^^^^^^
// if (err.code != "UserExists") throw err;
return admin.auth().updateUser(client.id, {
email: client.email
})
});
}

Node JS + AWS Promise Triggered Twice

AWS = require('aws-sdk');
AWS.config.region = 'eu-west-1';
ses = new AWS.SES();
var params = {};
return ses.sendEmail(params, function (err, data) {
console.log('----->sending email')
}).promise().then((data) => {
console.log('---->sending promise')
}).catch((err) => {
console.log('----->am in error')
console.log(err)
})
Can someone help my above code promise is triggered twice.
I should get below
----->sending email
---->sending promise
But i got
----->sending email
---->sending promise
----->sending email
It looks like you are providing both a callback function and using the promise approach. Effectively, this means that you have two different functions that execute when the request is done. Choose one of the following:
You can use the promise approach, with then and catch.
function send(params) {
ses.sendEmail(params).promise().then((data) => {
console.log('Email was sent')
}).catch((err) => {
console.log('There was an error')
})
}
You can use promise with async/await. Make sure you add the async keyword to your function header. Make sure your Node runtime supports async/await or that you code is transpiled to whatever Node version you are using.
async function send(params) {
try {
const data = await ses.sendEmail(params).promise();
console.log('Email was sent')
} catch(err) {
console.log('There was an error')
}
}
Finally, you could use the callback approach:
function send(params) {
ses.sendEmail(params, function(err, data) {
if (err) {
console.log('There was an error')
return
}
console.log('Email was sent')
})
}

How to set default rejected promise behavior for all my Express middlewares?

I'm using promises inside express middleware. I want to use the async/await methods.
app.get('/data1',async function(req,res) {
data = await getData1(); // This line throw an error,
res.send(data)
})
app.get('/data2',async function(req,res) {
data = await getData2(); // This line throw an error
res.send(data)
})
This makes the browser wait forever.
On the server I see
(node:251960) UnhandledPromiseRejectionWarning: Unhandled promise rejection
Now, to fix it for one middleware I'm doing:
app.get('/data1',async function (req,res){
return (async function(){
data = await getData1()
})().catch(() => {
res.send("You have an error")
}
})
app.get('/data2',async function (req,res){
return (async function(){
data = await getData2()
})().catch(() => {
res.send("You have an error")
}
})
I don't like this repetion. How can I set default error? I have tried for example:
app.use(function(error,req,res,next)){
res.send('You have an error')
}
But it didn't work.
In other words: How to set default function to be called when Express middlewares returning a rejected promise?
Now I found a way how to do it, I'm still keep the question open for more suggestions
app.get("/data1",
wrap_middleware(async (req, res) => {
data1=await getData1()
res.send(data1)
})
}
app.get("/data1",
wrap_middleware(async (req, res) => {
data2=await getData2()
})
}
function wrap_middleware(func) {
return async (req, res, next) => {
func(req, res, next).catch(err => {
console.log(err.message);
res.send("Error");
});
};
}
I don't understand the use of sending the same error for different function but I think the handling error code could be write in more readable way (just catch the error and do with them what you want the same way you catch errors in any route middleware):
function getData1(){
return new Promise( (resolve,reject) => {
setTimeout(() =>{
reject(new Error('error has occur!'));
},2000);
});
}
router.get('/data1', async (req,res, next) => {
try{
const data = await getData1();
res.send(data);
}
catch(ex){
res.send(ex.message);
// next(ex); => sending it to express to handle it
}
});
If you want a global error handling then its not any different from any code you want catch errors globally - you can set a function that take as param , the response object and the async code and create general catch for every async call comes from middleware (which has response object)

Express.js and Bluebird - Handling the promise chain

In a backend API I have a login route which should perform the following sequence of actions:
Given an username and password, try to authenticate the user against an Active Directory. If authentication has failed reply with status 401. If success, continue.
Look for an user with the given username in the database. If not found reply with status 403, otherwise continue.
Find if the user document has some details like email, display name, etc (in case this is not the first time logging in). If yes reply with the user object, otherwise continue.
Get user details from the Active Directory and update the user object in the database. Reply with the updated object.
Code:
router.post('/login', (req, res, next) => {
// capture credentials
const username = req.body.username;
const password = req.body.password;
let user = null;
// authenticate
ad.authenticate(username, password)
.then((success) => {
if (!success) {
res.status(401).send(); // authentication failed
next();
}
return User.findOne({ username }).exec();
})
.then((found) => {
if (!found) {
res.status(403).send(); // unauthorized, no account in DB
next();
}
user = found;
if (user.displayName) {
res.status(201).json(user); // all good, return user details
next();
}
// fetch user details from the AD
return ad.getUserDetails(username, password);
})
.then((details) => {
// update user object with the response details and save
// ...
return user.save();
})
.then((update) => {
res.status(201).json(update); // all good, return user object
next();
})
.catch(err => next(err));
});
Now I had this running with callbacks but it was really nested. So I wanted to give Bluebird promises a try, but I have two problems:
Looks chaotic, any better way to chain the calls and handle responses?
Whenever I call next() to stop the request after replying, the execution continues to the other .then(). Although the client receives the correct response, in the server log I find that the execution have continued. For example, if there is no account in DB for a given user, the client receives the 403 response but in the server log I see an exception failed to read property displayName of null, because there was no user and it should have stopped in the next() after res.status(403).send();.
Best use if/else to make clear what branches will execute and which won't:
ad.authenticate(username, password).then((success) => {
if (!success) {
res.status(401).send(); // authentication failed
} else {
return User.findOne({ username }).exec().then(user => {
if (!user) {
res.status(403).send(); // unauthorized, no account in DB
} else if (user.displayName) {
res.status(201).json(user); // all good, return user details
} else {
// fetch user details from the AD
return ad.getUserDetails(username, password).then(details => {
// update user object with the response details and save
// ...
return user.save();
}).then(update => {
res.status(201).json(update); // all good, return user object
});
}
});
}
}).then(() => next(), err => next(err));
The nesting of then calls is quite necessary for conditional evaluation, you cannot chain them linearly and "break out" in the middle (other than by throwing exceptions, which is really ugly).
If you don't like all those then callbacks, you can use async/await syntax (possibly with a transpiler - or use Bluebird's Promise.coroutine to emulate it with generator syntax). Your whole code then becomes
router.post('/login', async (req, res, next) => {
try {
// authenticate
const success = await ad.authenticate(req.body.username, req.body.password);
if (!success) {
res.status(401).send(); // authentication failed
} else {
const user = await User.findOne({ username }).exec();
if (!user) {
res.status(403).send(); // unauthorized, no account in DB
} else if (user.displayName) {
res.status(201).json(user); // all good, return user details
} else {
// fetch user details from the AD
const details = await ad.getUserDetails(username, password);
// update user object with the response details and save
// ...
const update = await user.save();
res.status(201).json(update); // all good, return user object
}
}
next(); // let's hope this doesn't throw
} catch(err) {
next(err);
}
});
To answer your second point, you have to reject your promise after calling next() (or at least return something, otherwise the line after will be executed). Something like
next();
return Promise.reject()
and change your catch so it works if you do not have an error
.catch(err => {
if (err)
next(err)
});
To your second question first: there is no way to break/stop a promise chain, unless your callback throw err like
doAsync()
.then(()=>{
throw 'sth wrong'
})
.then(()=>{
// code here never runs
})
You can simply try below demos to verify the second callback still runs.
doAsync()
.then(()=>{
res.end('end')
})
.then(()=>{
// code here always runs
})
doAsync()
.then(()=>{
return;
})
.then(()=>{
// code here always runs
})
To your first question: to use the second parameter in then(), which means reject. And each time split the logic to two parts.
var p = new Promise(function(resolve, reject) {
return
ad.auth(username, password).then(()={
// check if 401 needed. If needed, return reject
if (dont needed 401 in your logic)
resolve(username)
else
reject({ msg: 'authentication has failed', status: 401 })
})
});
p
.then( (username)=>{
// this only runs when the previous resolves
return User.findOne({ username }).exec()
}, (data)=>{
// in fact in your case you dont even have to have the reject callback
return data
} )
.then( (found)=>{
return
new Promise(function(resolve, reject) {
if (found && /*your logic to determine it's not 403*/)
resolve(user)
else
reject({ msg: 'unauthorized, no account in DB', status: 403 })
})
} )
.then( (found)=>{
return
new Promise(function(resolve, reject) {
if (found && /*your logic to determine it's not 403*/)
resolve(user)
else
reject({ msg: 'unauthorized, no account in DB', status: 403 })
})
} )
.then( (user)=>{
return
new Promise(function(resolve, reject) {
if (/*your logic to determine it has the full info*/)
resolve(user)
else
return ad.getUserDetails(username, password)
})
} )
.then( (user)=>{
// all is good, do the good logic
}, (data)=>{
// something wrong, so here you can handle all the reject in one place
res.send(data)
} )

ExpressJS: Promises and Error Handling middleware

I have some error handling middleware defined and a route returning a promise. But when that promise gives an error, I have to manually append .catch(err => next(err)) after every promise. While its not a problem, isn't it sensible for ExpressJs to see if a route returns a promise and if so call the error handling middleware automatically.
My current shortened code:
// errorHandlers.js
function sequelizeValidationError (err, req, res, next) {
if (err.name && err.name == 'SequelizeValidationError')
res.status(400).send(err.errors)
else next(err)
}
// auth.js
router.post ('/register', middleware.isNotAuthenticated, (req, res, next) => {
const { email, password, name } = req.body;
return models.User.find({where : { email }}).then(user => {
if (user) {
if (user.password == password) sendToken(user.id, res);
else res.sendStatus(401);
} else {
return models.User.create({
email, password, name
}).then(user => {
sendToken(user.id, res);
})
}
}).catch(next)
})
// index.js
router.use('/auth', require('./auth'))
router.use(errorHandlers.sequelizeValidationError)
For example, currently I could have forgot to write catch at one place and the server would have failed.
Am I missing out on something? How can I avoid having to type the catch every time?
This is already filed.
I had filed a duplicate bug
As of now the best bet seems to be to use a wrap function .
Also see #robertklep 's comment above. promise-express-router may be useful if you donot use route-params . express-co seems to be a wrap function + more generator-based goodness

Categories