gb_spectrum
gb_spectrum

Reputation: 2301

Mongoose - use findOne multiple times at once

Disclaimer: I am a newb web dev.

I am creating a registration page. There are 5 input fields, with 3 of them (username, password, and email) requiring that they pass various forms of validation. Here is the code:

router.post('/register', function (req, res, next) {
user.username = req.body.username;
user.profile.firstName = req.body.firstName;
user.profile.lastName = req.body.lastName;
user.password = req.body.password;
user.email = req.body.email;

User.findOne({email: req.body.email}, function(err, existingEmail) {

    if(existingEmail) {
        console.log(req.body.email + " is already in use")
    } else {
        User.findOne({username: req.body.username}, function(err, existingUsername) {
            if(existingUsername) {
                console.log(req.body.username + " is already in use");
            } else {
                user.validate({password: req.body.password}, function(err) {
                    if (err) {
                        console.log(String(err));
                    } else {
                        user.save(function(err, user) {
                            if (err) {
                                return next(err);
                            } else {
                                return res.redirect('/')
                            }
                        })
                    }
                });
            }
        });
    }
});
});

Basically it first checks to see if it is a duplicate e-mail; if it is a duplicate e-mail, it says so in the console.log. If it isn't a duplicate e-mail, it then checks the username.... and then goes onto the password.

The issue is that it does this all one at a time; if the user inputs an incorrect email and username, it will only say that the email is incorrect (it won't say that both the email and username are incorrect).

How can I get this to validate all 3 forms at the same time?

Upvotes: 0

Views: 749

Answers (2)

war1oc
war1oc

Reputation: 2755

You can use async to run them in parallel and it will also make your code cleaner and take care of that callback hell:

var async = require('async');

async.parallel([
  function validateEmail(callback) {
    User.findOne({email: req.body.email}, function(err, existingEmail) {
      if(existingEmail) {
        callback('Email already exists');
      } else {
        callback();
      }
    }
  },
  function validateUsername(callback) {
    User.findOne({username: req.body.username}, function(err, existingUsername) {
      if(existingUsername) {
        callback('Username already exists');
      } else {
        callback();
      }
    }
  },
  function validatePassword() {
    user.validate({password: req.body.password}, function(err) {
      if(err) {
        callback(err);
      } else {
        callback();
      }
    }
  }
], function(err) {
    if(err) {
      console.error(err);
      return next(err);
    } else {
      user.save(function(err, user) {
        if (err) {
          return next(err);
        } else {
          return res.redirect('/');
        }
      });
    }
  }
);

This way, all the validation methods inside the array will be run in parallel and when all of them are complete the user will be saved.

Upvotes: 1

MarcoS
MarcoS

Reputation: 17721

If you use else statements, you choose to make checks individually (one at a time) by design.

To achieve an 'all at once' behaviour, I would not use else statements (where possible, i.e. errors ar not fatal for next checks), but would do all tests in the same block, and would fill an object like this:

errors: {
  existingEmail: false,
  existingUserName: false,
  invalidUserName: false,
  wrongPassword: false,
  ...
};

And then I'd use it in the form to show user all errors together...

Something like this:

var errors = {};
if (existingEmail) {
    console.log(req.body.email + " is already in use");
    errors.existingEmail: true;
}
User.findOne({username: req.body.username}, function(err, existingUsername) {
    if (existingUsername) {
        console.log(req.body.username + " is already in use");
        errors.existingUsername: true;
    } else {
        user.validate({password: req.body.password}, function(err) {
            if (err) {
                console.log(String(err));
                errors.invalidUsername = true;
            } else {
                user.save(function(err, user) {
                    if (err) {
                        return next(err);
                    } else {
                        return res.redirect('/')
                    }
                })
            }
        });
    }
});

Upvotes: 0

Related Questions