Reputation: 7282
I'm starting with Promise chaining and I'm trying some code using express and mongoose in node. This is my code:
const register = (req, res, next) => {
User.findOne(req.params.email).exec().then(user => {
if (user) {
return res.status(409).json({ error: 409 });
}
return User.create(req.body);
}).then(user => {
const token = jwt.sign({ sub: user._id }, 'test-secret');
return res.status(200).json({ token });
}).catch(err => {
return next(err);
});
};
This is a simplified code to register a user and send him a token. What I want to do is, first, check if the user is already registered and, if not, register it.
As you can see, the line 6 is wrong I think, because I'm not returning any Promise, so after the line 4, the code continuos executing. I want to avoid the callback hell, how can I accomplish this? Thanks
Upvotes: 1
Views: 65
Reputation: 76929
The fact that you're not returning a Promise
is not important. Return values from then()
and catch()
are automatically wrapped in Promise
objects.
The problem is that your flow is linear (it follows a straight path), but you are trying to branch so that some parts only run in some cases. You need two execution paths:
const register = (req, res, next) => {
User.findOne(req.params.email).exec().then(user => {
if (user) {
// Path 1 stops here:
return res.status(409).json({ error: 409 });
} else {
// Path 2 continues down this 2nd Promise chain:
return User.create(req.body).then(user => {
const token = jwt.sign({ sub: user._id }, 'test-secret');
return res.status(200).json({ token });
})
}
}).catch(err => {
// Both paths converge on this error handler
return next(err);
});
};
If you want to avoid deep nesting, you have two options:
Encapsulate the different paths into functions, so you can then do something like this:
if (user) {
return sendHttpError(res, 409)
} else {
return sendNewUser(res, req.body)
}
Perhaps only the sendHttpError
function is necessary, and the other possible path can be the main body of your route. This is pretty standard.
Since one of your branches is only dealing with error cases, you can throw an Error
and catch it below, or in an error-handling middleware. It would look like this:
if (user) {
// This throw will abort execution of everything that follows. By
// using a custom Error class, you can then handle it appropriately
// in a catch() handler or Express middleware:
throw new APIError(409)
}
// ... create user and proceed normally
This is also a very common pattern.
Upvotes: 3
Reputation: 7605
Just throw an error
if the user already exists to go directly to the catch
function.
const register = (req, res, next) => {
User.findOne(req.params.email).exec().then(user => {
if (user) {
throw new Error({ error: 409, msg: 'User already exists' });
}
return User.create(req.body);
}).then(user => {
const token = jwt.sign({ sub: user._id }, 'test-secret');
return res.status(200).json({ token });
}).catch(err => {
return next(err);
});
};
Upvotes: 2