Promise chaining with mongoDB (and mongoose). How to use .save() after .then() and correctly breakout of a promise chain if a response has been sent?
You could solve this somehow by chaining promises correctly in a more complicated way, or you use async
/ await
and get rid of all those problems:
router.post('/', async (req, res) => { try { //Step 1: validae the user input and if there is an error, send 400 res and error message console.log('My user post body req::', req.body); const { error } = validateUser(req.body); //this is using Joi.validate() which has a error property if errors are found if (error) { return res.status(400).send(error.details[0].message); } //step 2: check if user already exists, if yes send res 400 let user = await User.findOne({ email: req.body.email }); if (user) { return res.status(400).send('User already exists'); } //Step 3: enter new user into the database user = new User({ name: req.body.name, email: req.body.email, password: req.body.password }); await user.save(); //step 4: return the newly added user return res.status(200).send(user); } catch(error) { // Report error internally return res.status(500).send("Something bad happened"); } });
The main problem with your code is that return
ing from a .then
callback will continue executing the next .then
callback. Therefore you try to set the headers status multiple times (but that's your smallest problem).
You don't need to use Promise.resolve in your route.You just need a chain of then blocks, in which you need to return a value to the next one.
I refactored your code like this:
router.post("/", (req, res) => { //Step 1: validate the user input and if there is an error, send 400 res and error message console.log("My user post body req::", req.body); const { error } = validateUser(req.body); if (error) { return res.status(400).send(error.details[0].message); } //step 2: check if user already exists, if yes send res 400 User.findOne({ email: req.body.email }) .then(user => { if (user) { return res.status(400).send("User already exists"); } return; }) .then(() => { //Step 3: enter new user into the database let user = new User({ name: req.body.name, email: req.body.email, password: req.body.password }); return user.save(); }) .then(result => { //step 4: return the newly added user return res.status(200).send(result); }) .catch(error => { console.log("Error Adding new User", error); res.status(500).send("Error"); }); });
You will have a result like this when a user successfully registers:
{ "_id": "5dd65df52f7f615d8067150d", "name": "ssaquif", "email": "test@test.com", "password": "123123", "__v": 0}
And when an existing email is used, the response will be like this with statusCode 400.
User already exists
If you look at the error message "Cannot set headers after they are sent to the client" it means you are trying to send something over the response object twice which are not possible. Try log something right before each time you send something as a response and see which two are being called.
Instead of returning the res.status(400).send promise, try call it normally and then return a rejected promise or throw an error instead.