Using rejected promises to handle bad HTTP calls? - node.js

So, I have the following code:
function SignUp(req, res, next){
const userCreds = {
email: req.body.email,
password: req.body.password
}
//Username and password must exist
if(!userCreds.email || !userCreds.password){
res.status(422).send({ error: 'Email and Password required'});
throw new Error(('Email and Password Required'));
}
//See if email is already being used
Users.findOne({ email: userCreds.email })
.then(function(user){
//If user does exist, return Error
if(user){
res.status(422).send({ error: 'Email is in use'});
throw new Error(('Email and Password Required'));
}
//Else if email is true, create and save user error
const newUser = new Users(userCreds);
//Save the user
return newUser.save(); //Return promise
})
.then(function(doc){
//Respond saying all OK
res.json({
success: true,
email: doc.email
});
})
.catch(function(err){
if(err)
return next(err);
});
}
The function above is passed to an Express route, like this
app.get('/signup', SignUp);
In this code, there are two different 'errors' that can occur and I need to handle. One kind of error is that the user request cannot be processed (Trying to create an account without supplying both Email and Password, or using an Email that already is being used). The second kind of error is one that I have less control in: rejected promises from the Mongoose package.
Let's say that I have received a bad request, an error of type 1. I want to handle it by setting the header of the response to 422, and sending that response with a message detailing why it could not be processed. At that point the execution would end.
If I get an error of type 2, I want to call next(error) and stop execution at that point.
The problem is, by chaining .then() functions, I cannot return from a block of code without jumping into the following .next().
One way to get around this is by throwing an error via throw new Error() when I get an error of either type 1 or 2, and handle the case in .catch(), but I am unsure how much of a good or bad practice this would be.
How can I make it so that I can handle the error in a .then() block and then stop execution? And would that be the best way to do it?
Is there a better way to handle these kind of situations in Express? Am I missing anything?
Thank you!

A solution would be to create an Error subclass (for instance, using error-subclass) and throw instances of those in case you want to signal a processing error.
Subsequently, in the .catch() handler you'd check if the error is an instance of that custom error class, and if so, return a 422 response. If not, pass it to next instead:
const ErrorSubclass = require('error-subclass').default;
class ProcessingError extends ErrorSubclass {}
Users.findOne({ email: userCreds.email })
.then(function(user){
if (user) {
throw new ProcessingError('Email and Password Required');
}
...
}).catch(function(err) {
if (err instanceof ProcessingError) {
return res.status(422).send({ error: err.message });
}
next(err);
});

Related

how to Handle promise error in nodeJS / express

I'm building a nodeJS authentication API, I found a lot of resources on GitHub, I'm confused about writing and hundle promise error and I want to understand it to change some code on the cloned project.
The first function is register located on register service :
async function register(params, origin) {
// validate
if (await db.Account.findOne({ email: params.email })) {
// send already registered error in email to prevent account enumeration
return await sendAlreadyRegisteredEmail(params.email, origin);
}
// create account object
const account = new db.Account(params);
// first registered account is an admin
const isFirstAccount = (await db.Account.countDocuments({})) === 0;
account.role = isFirstAccount ? Role.Admin : Role.User;
account.verificationToken = randomTokenString();
// hash password
account.passwordHash = hash(params.password);
// save account
await account.save();
// send email
await sendVerificationEmail(account, origin);
}
when an account is already registered I want to return an error not sending an email ( line 5 )
account controller
here is the controller where I want to handle the promise returned from account service :
router.post('/register', registerSchema, register);
function register(req, res, next) {
accountService.register(req.body, req.get('origin'))
.then(() => res.json({ message: 'Registration successful, please check your email for verification instructions' }))
.catch(next);
}
I thought that .catch function trait a rejected promise isn't it ? what does .catch(next) do exactly ? and in case where an account exist already when register how to return an api error with a status code ?
You are mixing 2 ways of handling promise in Javascript in one
.catch is to handle promise rejection from .then block.
With the async-await syntax, we need to handle promise rejection with a try-catch block.
In your register function just wrap all await part in one try-catch block and it will be handled.
You could handle an "account exists" condition as an error, using throw & catch.
However, I'd make the register function return a status message or other indicator to the caller so it can be used in a more flexible & reusable way.
async function register(params, origin) {
// validate
if (await db.Account.findOne({ email: params.email })) {
// send already registered error in email to prevent account enumeration
return {success: false, await sendAlreadyRegisteredEmail(params.email, origin)};
}
// create account object
const account = new db.Account(params);
// first registered account is an admin
const isFirstAccount = (await db.Account.countDocuments({})) === 0;
account.role = isFirstAccount ? Role.Admin : Role.User;
account.verificationToken = randomTokenString();
// hash password
account.passwordHash = hash(params.password);
// save account
await account.save();
// send email
return {success: true, await sendVerificationEmail(account, origin)};
}
And then you could handle the message here:
function register(req, res, next) {
accountService.register(req.body, req.get('origin'))
.then((results) => {
if (results.success) {
res.json({ message: 'Registration successful, please check your email for verification instructions' })
} else {
// send failure message
}
})
.catch(next);
}

Best way to handle multiple functions/scenarios inside an express route?

I have many endpoints in my express app that have many conditions. I want to find what is the best design pattern for them without repeating myself so much.
This is one of my simpler routes:
router.post('/reset/:token',
asyncMiddleware(async(req, res, next) => { await reset(req, res, next, pino); })
);
Inside reset()I need to check a couple of things, such as:
If all the required body params are there
If the email from the decrypted token matches the one from the database
If the password was saved successfully.
I would like to check those conditions that without having a huge function, but I don't know what is the best way to do so.
Entire Route Code
export async function reset(req, res, next) {
const email = req.body.email;
if (!email) return res.status(400).json(Error.paramsMissing('email'));
const user = await userAssociatedWithEmail(req.body.email);
if (!user) {
return res.status(501).json(Error.noActiveUserAssociatedWithEmail);
}
// Generate token
const token = await jwt.sign({ email: user.email, id: user.id }, 'shhhhh');
const emailSent = await sendForgotEmail(token, user);
if (!emailSent) return res.status(500).json(Error.emailNotSent);
else return res.json({ status: 'success', message: 'Email sent successfully.' });
}
What I would like to do
Final Result I'd like to have
export async function reset(req, res, next) {
const email = req.body.email;
if (!email) return res.status(400).json(Error.paramsMissing('email'));
// If error inside userAssociatedWithEmail, I'd like to stop execution and
// return res.status(501).json(Error.noActiveUserAssociatedWithEmail) from inside
// that function, without having to add an if condition below as exists in the
// original code above
const user = await userAssociatedWithEmail(req.body.email);
const token = await jwt.sign({ email: user.email, id: user.id }, 'shhhhh');
// Again I'd like to return res.status(500).json(Error.emailNotSent)
// from inside sendForgotEmail IF there is an error
const emailSent = await sendForgotEmail(token, user);
// If everything is successful, finally I'd return this
return res.json({ status: 'success', message: 'Email sent successfully.' });
}
Explanation of the result in word:
I'd like to be able to handle the conditions and scenarios without having to handle it in the main reset function if that's possible (aka, without having to store a response in a variable, check the variable and return in the main function in the case of error).
So for example, instead of:
const allParamsAreValid = validParams(token, email, new_password, res);
if (!allParamsAreValid) return;
I'd like to do something like:
validateParams(token, email, new_password, res);
And then inside validateParams() if a param is missing, I'd force exit the program besides also setting the response with res.json({}).
Is that possible?
You can make all your asynchronous functions that return promises reject their promise with the status and value you want sent. Then, you can handle that rejected promise in one place:
export async function reset(req, res, next) {
try {
const email = req.body.email;
if (!email) return res.status(400).json(Error.paramsMissing('email'));
// If error inside userAssociatedWithEmail, I'd like to stop execution and
// return res.status(501).json(Error.noActiveUserAssociatedWithEmail) from inside
// that function, without having to add an if condition below as exists in the
// original code above
const user = await userAssociatedWithEmail(req.body.email);
const token = await jwt.sign({ email: user.email, id: user.id }, 'shhhhh');
// Again I'd like to return res.status(500).json(Error.emailNotSent)
// from inside sendForgotEmail IF there is an error
const emailSent = await sendForgotEmail(token, user);
// If everything is successful, finally I'd return this
res.json({ status: 'success', message: 'Email sent successfully.' });
} catch(e) {
res.status(e.status || 500).json(e.errData)
}
}
And, then all of your asynchronous functions would reject if they have an error condition and set both e.status and e.errData on the rejected reason. That would allow you to have one common error handler and let the async function collect any rejected promise into your try/catch for you. This is meant to be the clean way you handle rejections in a series of await calls where you want the whole function to finish.
Then, you also need to make sure your asyncMiddleware() function is NOT also sending a response (can't really tell what its purpose is). You don't show that code, so I can't see what it's doing.
You don't show any code that uses validateParams(), but if it was synchronous, then it could just throw an exception with the right fields set on it and the try/catch would also catch it just like it would catch the async rejections.
For example:
function validateParams(token, email, new_password) {
let err = new Error();
err.errData = {status: 'error'};
if (!token) {
err.errData.message = 'invalid token';
throw err;
}
if (!email) {
err.errData = Error.paramsMissing('email');
throw err;
}
if (!new_password) {
err.errData.message = 'invalid new password');
throw err;
}
}
If you wanted to, you could also send an error response in validateParams(), but I think it's cleaner not to because they you can collect all errors including all your await asynchronous calls in one try/catch in the route handler and frankly, it's a lot more readable and understandable code not to send a response in some function calls, but not in others. I try to keep all my responses both error and success sent at the same level. Then, it's really easy to keep track of and to avoid accidentally trying to send multiple responses.
Then, in your route handler, you'd just call validateParams(...) just like that. If it throws, your try/catch would catch it and send the appropriate error. If no error, then execution would just continue.
Since you are passing res object to the validateParams method, you can do something like this,
async function validateParams(token, email, new_password, res) {
if (token && emal && new_password && res) {
// all values are available
// perform your desired operation
} else {
// exit from the method and pass info to the client
return res.json({ message: 'Invalid parameter' });
}
}
In this case, all you have to do is invoking the validateParams.
await validateParams(token, email, new_password, res);
If there is a missing parameter, the server passes the control to the client immediately. Otherwise, you can perform your operation there.

firebase temporarily sending "There is no user record"?

I'm trying to make a simple web application and trying to implement google authentication(login) using passport and store the user in firebase authentication.
So, in the passport middleware what i do is check if the user is authenticated.
if yes then pass the user to the passport.serializeUser(user) else create the user in firebase authentication. and then pass the user to passport.serializeUser(user)
here is a pseudo code -
if (user.authenticated){
done(null, user)
} else {
let promise = {'uid': user.uid, 'name': user.displayName, 'picture':user.photos[0].value};
firebase.auth().createUser({
uid: user.uid,
displayName: user.name,
photoURL: user.picture
});
console.log('i have submitted the user')
done(null, promise)
}
everything is going cool the passport.serializeUser() gets and the user calls done(null, user.uid).
the problem hits when the passport.deserializeUser().
i dont do any fancy there but just get the user from the firebase by using firebase.auth().getUser(id) where it gives me an error.
here is my code for passport.deserializeUser().
passport.deserializeUser((id, done) => { // when we get a request
console.log(`deser id ${id}`); // it shows the id we passed in serialize
firebase.auth().getUser(id).then( // gives an error T_T ?
(user) => {
console.log(`deser data ${user}`); // IT SHOULD GO HERE
done(null, user)
}).catch((error) => { // it goes here and throws an error at me
console.log(`here is the error on deser ${error}`);
// here is the error on deser Error: There is no user record corresponding to the provided identifier.
});
});
the magic happens after some time when the passport.deserealizeUser() gets called again at now for some reason it doesn't throw a error at me.
here is the console log..
i have submitted the user
ser // i do this call in passport.serializeUser()
deser id "someid"
here is the error on deser Error: There is no user record corresponding to the provided identifier.
deser id "someid"
deser data [object Object]
my question is that why does the firebase being delayed?
is it because of the reason that first time the firebase didn't loaded and the second time it loaded and was successful to find the user?
but what could be the reason for that?
any guesses?
Alright guys So, after a big nap I've found out what I did wrong, i wasn't handling the promise from the..
firebase.auth().createUser({
uid: user.uid,
displayName: user.name,
photoURL: user.picture
});
so what i did is like ..
firebase.auth().createUser({
uid: user.uid,
displayName: user.name,
photoURL: user.picture
}).then((user) => {
done(null, user);
});
certainly it requires some time to process the user in the auth() ..
that is why.
ALWAYS USE PROMISES.

MEAN 2 Multi Tenancy (Multiple collection saves that reference each other)

So whilst learning JS and specifically the MEAN 2 stack i'm trying to build out a basic multi tenanted app. Im building out sign up routes in express and the flow i'm trying to achieve would be:
Sign up with company name, email and password. The info would go to save a new tenant, then return the _id of the new tenant and then use this new id, the email and the password to save a new user.
The closest is:
router.post('/', function (req, res, next) {
var tenant = new Tenant({
name: req.body.name
});
var newTenant;
tenant.save(function (err, tenant) {
if (err) {
return res.status(500).json({
title: 'An error has occured',
error: err
});
}
res.status(201).json({
message: 'Tenant created',
obj: tenant
});
return(tenant._id);
newTenant = tenant;
});
Tenant.findById(newTenant._id, function(err, tenant) {
if (err) {
return res.status(500).json({
title:'An error occured',
error: err
});
}
var user = new User({
email: req.body.email,
password: bcrypt.hashSync(req.body.password, 10),
active: req.body.active,
tenant: tenant
});
user.save(function (err, user) {
if (err) {
return res.status(500).json({
title: 'An error has occured',
error: err
});
}
res.status(201).json({
message: 'User created',
obj: user
});
});
});
});
module.exports = router;
I'm getting an error: cant set headers after they've been sent.
I think I know where i'm wrong, with returning the tenant info. I think Async is the answer but cant figure out how to implement it. Sorry if this is a stupid question or i'm missing something obvious, I'm super new to this and callbacks are doing my head in.
This is happening because res.status() sets headers as soon as it has fired. You try to do this multiple times both when checking for errors, and then you try to set the status code again in Tenant.findById().
You end up with a flow like:
if (err) set headers
set headers (again)
findById()
if (err) set headers
set headers (again)
You have to be careful when writing out your response that you only do it at the last point in your logic flow. You can also could set up a global err handler and throw new Error() and stop the flow of logic and handle the output immediately. If you don't, your code will continue to execute even though it encountered an error.
Another tip: callbacks don't work well with returns. And although you can arrange them to work, or implement a promise architecture instead, the simplest fix (and the easiest to learn) is to make your functions all asynchronous.
Try instead something like:
tenant.save(function (err, tenant, callback) {
// add a callback param to your fn ^
if (err) {
throw({
code: 500,
title: 'An error has occured',
error: err
});
} else {
// ^ add an else statement so you don't set the headers twice
// (because res.status() sets headers)
res.status(201).json({
message: 'Tenant created',
obj: tenant
});
}
callback(err, tenant);
// call your async function instead of return,
// and pass both err and tenant as params
// (one or the other may be undefined though, if it fails/succeeds)
});
... Create additional isolated functions (or even modules) for the rest of your tasks, then you can then call your function like this:
tenant.save(function(err, tenant) {
Tenant.findById(tenant._id, function(err, tenant) {
var user = new User({...})
user.save()
});
});

How to handle and return errors in an API using NodeJS / Express

Im creating an a API using NodeJS with the express framework and mongodb to store my data.
I have a register function which does 3 main things.
Creates the new.
Creates a token and associates it with a user.
Sends an email.
module.exports.register = function(req, res) {
var input = req.body;
var token = uuid.v4();
// Create a new user
var user = new User ({
username: input.username,
email: input.email,
password: input.password,
active: false
});
user.save(function(err) {
if(err) return res.json({success: false, errors: 'Failed To Create User'});
});
// Create a new Token
var newToken = createToken('new', null, user._id);
// Assign New Token To New User
if(newToken) {
user.tokens.push(newToken._id);
user.save(function(err) {
if(err) return res.json({success: false, errors: 'Failed To Save User Token'});
});
}
// Send Email To User
var mailData = {
from: 'deleted#hotmail.com',
to: input.email,
subject: 'Activate Your Account',
text: 'http://localhost:8080/api/auth/activate/' + token
}
mail.messages().send(mailData, function(err, body) {
if(err) return res.json({ success: false, errors: 'Failed To Send Email' });
});
return res.json({
success: true,
status: 'Successfully Registered User, Check Email To Activate'
});
}
Now even if there are errors whilst creating the user or the token or sending an email. It's always going to return that it successfully registered a user. How can i restructure / handle this better?
I also have the problem where if the email fails to send the user and token will have already been created, how do i solve this issue? Would i just create a resend activation function?
You mention that it's always going to return that it successfully registered a user. It will also send the email even if the token creation failed.
One (not very pretty) way to do it would be to continue with the next step inside the callback function of the previous step:
user.save(function(err) {
if(err) {
return res.json({success: false, errors: 'Failed To Create User'});
} else {
// Create a new Token
var newToken = createToken('new', null, user._id);
// Assign New Token To New User
if(newToken) {
user.tokens.push(newToken._id);
user.save(function(err) {
if(err) {
return res.json({success: false, errors: 'Failed To Save User Token'});
} else {
// Send Email To User
var mailData = {
from: 'deleted#example.com',
to: input.email,
subject: 'Activate Your Account',
text: 'http://localhost:8080/api/auth/activate/' + token
}
mail.messages().send(mailData, function(err, body) {
if(err) {
return res.json({ success: false, errors: 'Failed To Send Email' });
} else {
return res.json({
success: true,
status: 'Successfully Registered User, Check Email To Activate'
});
}
});
}
});
}
}
});
As you can see, it looks like a callback-piramyd-of-doom very fast, but it only sends the success response when all the previous steps have completed.
You should also add the else case when the newToken is not created.
You should remove final return statement (from the end of your code) and return at the correct place inside each callback if there is no error.
If you send your response in the body of the function your callbacks will never get the chance to run. Therefore you must nest your callbacks and only call res.send if you are
returning early due to an error or
if everything is complete.
e.g.
// Create a new user
var user = new User ({
username: input.username,
email: input.email,
password: input.password,
active: false
});
user.save(function(err) {
if(err) return res.json({success: false, errors: 'Failed To Create User'});
// Create a new Token
var newToken = createToken('new', null, user._id);
// Assign New Token To New User
if(newToken) {
user.tokens.push(newToken._id);
user.save(function(err) {
if(err) return res.json({success: false, errors: 'Failed To Save User Token'});
// Send Email To User
var mailData = {
from: 'deleted#hotmail.com',
to: input.email,
subject: 'Activate Your Account',
text: 'http://localhost:8080/api/auth/activate/' + token
}
mail.messages().send(mailData, function(err, body) {
if(err) return res.json({ success: false, errors: 'Failed To Send Email' });
return res.json({
success: true,
status: 'Successfully Registered User, Check Email To Activate'
});
});
});
}
});
Asynchronous alternatives
Unfortunately, with node.js you should get used to and understand callbacks; even if you end up using something else most of the time. The way your code was structured was neater and logical but does not work in node.js because you have to wait for the callbacks to complete before you can return from your function.
However, callbacks are the default but one of the worst mechanisms for handling asynchronous logic. If you want to structure the code differently you have quite a few options. Here are just a couple:
Use promises instead of callbacks.
In your case your database library (mongoose? sequelize?) should have something built in that allows you to write your code like this:
user.save()
.then(function () {
// step 1
})
.then(funciton () {
// step 2
})
.done()
This style of programming is well worth learning and will make your code more readable than callbacks. callbacks vs promises
Use Koa instead of express.
Koa, is the next generation of express written by the same people. It uses generators instead of callbacks which means you can write code that looks more like this:
// this is just an example
var result = user.save();
if (result.error) return res.send({success : false, ...});
user.token = getNewToken();
user.update();
if (result.error) return res.send({success : false, ...});
return res.send({success : true, message : "Good news, no errors"});
Generators/(aka async functions) are the direction Javascript is moving in but there is a learning curve to start using. Behind the scenes there is something very complex going on to make asynchronous code appear exactly like synchronous code. Basically, the functions know how to pause execution until they are required again.
Start with callbacks
Like I say, callbacks are not that nice. However, you should get used to using them. They are the basic building blocks of node.js and it take a while to get comfortable with better alternatives. It's also important to get used to them because otherwise you won't appreciate why the alternatives are better.
Good luck and watch out for callbacks inside loops :)

Resources