ExpressJS: Promises and Error Handling middleware - javascript

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

Related

Difference between async(req,res,next) and async(req,res,()=>{}

I recently came across this code and I fail to understand why the next has been omitted from the protect function(inside protectandauth function) while it is included in the protect function originally.
I want to know the difference between protect=async(req,res,next)and protect=async(req,res,()=>{}.
I also see that even though next is omitted in the protect(the one inside protectandauth) function, it is still used in the code after the 'if' statement, how is that possible?
Code:
export const protect = async (req, res, next) => {
if (
req.headers.authorization &&
req.headers.authorization.startsWith("Bearer")
) {
let token;
token = req.headers.authorization.split(" ")[1];
const decoded = jwt.verify(token, "kris");
req.userId = decoded.id;
try {
req.user = await User.findById(req.userId).select("-password");
next();
} catch (error) {
res.status(401).json(error.message);
}
if (!token) {
res.status(404).json("no token found");
}
}
};
export const protectandauth = async (req, res, next) => {
protect(req, res, () => {
if (req.userId == req.params.id) {
next();
} else {
res.status(401).json("not authorised");
}
});
};
Every callback where you access req and res, you can also access next. next is a function that's used to to say "pass to the next callback", knowing that a request can be processed by multiple callbacks, like so:
const firstCallback= (req, res, next) => {}
const secondCallback= (req, res, next) => {}
app.get("/", firstCallback);
app.get("/", secondCallback);
// or using this syntax
app.get("/", firstCallback, secondCallback);
In the above example, when a request comes to /, it's handled first by firstCallback, and it's one of the two below scenarios (otherwise the request will hang, and the user won't get a response):
It stops the request by calling one of the res methods, like res.status(401).json("not authorised");
It says "pass to the next callback" calling next(), and then secondCallback handles it.
If next is omitted from the parameters, you will be calling next() where it's undefined, and that throws an error. Speaking of the use of protect function, if you notice, there is next as part of protectandauth's parameters, and it's that next that's used inside protect's third parameter, which is:
() => {
if (req.userId == req.params.id) {
next();
} else {
res.status(401).json("not authorised");
}
}
And in this specific code you have, the above function is passed as next in protect's definition.
We use next if we want to pass our request to the next middleware in line. Maybe in protect, the programmer might not want to pass the req to the next middleware but in protectandauth he want to pass the req to the next middleware if this condition turns out to be true
if (req.userId == req.params.id) {
next();
}

Express email error handling, for nested middleware

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

Why error message is displayed on console when controller throws a method supertest

I am writing integration test for a nodejs/sails js application, where I have an Async controller method/route that throws error when input parameters are not provided.
I am using supertest to write integration test, everything works fine from my perspective, but when the test run error is written onto the console.
describe("a controller method", () => {
it("should throw error message", () => {
server('127.0.0.1')
.get('url to getData')
.set('Cookie', 'cookie token')
.end((err, res) => {
if(err) return done(err);
//console.log(res);
expect(res.status).to.equal(500);
expect(res.body).to.have.property('error', 'Provide a jobName');
done();
});
});
});
This following piece of code works fine cause I wrap this within an anonymous function and expect that function to throw. But I am not sure how to assert against those error.
it("throws", () => {
expect( function () {
server('127.0.0.1')
.get('some url')
.set('Cookie', 'cookie token')
}).to.throw;
});
The controller code looks like following. This is the method that is being called when URL end is requested.
getData : async (req, res) => {
let jobName = req.params.jobName || '',
buildId = req.params.buildId || '';
if(!jobName.trim() ){
return res.negotiate({error:'Provide a jobName'});
}
if(isNaN(buildId)) {
return res.negotiate({error:'Invalid build id supplied.'});
}
try {
let rawResult = await getData(jobName, buildId);
let promotions = formatData(rawResult);
let result = {
total : promotions.length || 0,
items : promotions
};
return res.json(result);
} catch(error) {
sails.log.error(`Request Parameter: Job name = ${req.param('jobName')} & build id = ${req.param('buildId')}`);
sails.log.error(error);
return res.negotiate({error: sails.config.errorMessage});
}
}
Why is the error being written to console ? What am I doing wrong here?
Any help/pointer is highly appreciated!!
How are you sending that error from express? In general express follows the way of passing errors rather than throwing it, for any errors in the program you can pass the error object to 'next' function. http://expressjs.com/en/guide/error-handling.html
e.g
app.use(function (err, req, res, next) {
console.error(err.stack)
res.status(500).send('Something broke!')
})
async returns a promise and although you have placed a try catch block, my guess is the error is not in the block. It is most likely due to the promise rejection not getting handled.
The below should be of help to you as applying to the sails context.
since async functions return a promise, you need to .catch() any
promise rejections and pass them along to next(). Express error
handlers are only triggered by errors passed to next(), not exceptions
that you throw. Source
And for promise()=> reject

How could I generalize a http response handler in Node.js?

I'm writing a rest api for a node application, and I find myself rewriting something like the following a lot:
function(req, res, next) {
databaseCall()
.then( (results) => {
if (results != null) {
res.status(200).send(results);
} else {
res.sendStatus(404);
}
})
.catch(function(err) {
console.log("Request error: " + err.stack);
res.sendStatus(500);
})
}
I would like to refactor the response portion, so I can do something like
databaseCall()
.then(handleResponse)
where handleResponse would take care of the whole response/catch process.
But I can't quite figure out how to do that. The databaseCall method varies depending on the endpoint - sometimes it takes a parameter, sometimes not. I could make a generic function expression that takes the databaseCall result and stick it in the promise chain, but I don't know how I could access the response object inside that function. I know I could add another function to combine everything, like so:
function(databaseCall, parameter, req, res, next) {
databaseCall(parameter)
.then( (results) => {
if (results != null) {
res.status(200).send(results);
} else {
res.sendStatus(404);
}
})
.catch( (err) => {
console.log("Request error: " + err.stack);
res.sendStatus(500);
})
}
But that seems ugly since databaseCall could have 0-several parameters. I'd think there's a more elegant solution.
You're probably thinking in the right direction, you just need to take it a step further and keep the db call outside the generic handler, and pass it as a promise instead
// generic handler for db promise
// the promise is created outside and passed as arg
function responseFromDb(databaseCallPromise, res) {
databaseCallPromise
.then((results) => {
if (results != null) {
res.status(200).send(results);
} else {
res.sendStatus(404);
}
})
.catch((err) => {
console.log(`Request error: ${err.stack}`);
res.sendStatus(500);
});
}
// handler per request, only needs to create db call with the desired params
// and pass it to the generic handler, which will take care of sending the response
function(req, res, next) {
responseFromDb(databaseCall(param1, param2), res)
}

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

Categories