Question

I've been playing around with Sails for maybe one day. I'm trying to wrap my head around what would be the best way to do extensive validation in Sails.js.

Here is the scenario:

Registration Form:

Username: _______________
E-Mail:   _______________
Password: _______________
Confirm:  _______________

User inputs:

  • a correct e-mail
  • a username that already exists
  • two passwords that don't match

Desired outcome:

Username: _______________ x Already taken
E-Mail:   _______________ ✓
Password: _______________ ✓
Confirm:  _______________ x Does not match

Requirements, a few key points:

  • The user receives all error messages (not just the first one) for every aspect of his input. They are not vague ("username already taken" or "username must be at least 4 letters long" is better than "invalid username")
  • The built-in model validation can obviously not be responsible for checking a matched password confirmation (SRP)

What I think I need to do:

UserController:

create: function(req, res) {
    try {
        // use a UserManager-Service to keep the controller nice and thin
        UserManager.create(req.params.all(), function(user) {
            res.send(user.toJSON());
        });
    }
    catch (e) {
        res.send(e);
    }
}

UserManager:

create: function(input, cb) {
    UserValidator.validate(input); // this can throw a ValidationException which will then be handled by the controller
    User.create(input, cb); // this line should only be reached if the UserValidator did not throw an exception
}

User: (model)

attributes: {
    username: {
        type: 'string',
        required: true,
        minLength: 3,
        unique: true
    },

    email: {
        type: 'email',
        required: true,
        unique: true
    },

    password: {
        type: 'string',
        required: true
    }
}

UserValidator:

This is the tricky part. I need to combine input-specific validation (does the password confirmation match?) with the Model validation (is the username taken and is the e-mail address valid?).

If there was a way to instantiate a User-model and perform validation without saving to the database in Sails/Waterline I think this would be quite straight-forward, but there doesn't seem to be that option.

How would you go about solving this problem? Thank you very much for your help!

Was it helpful?

Solution 2

There are going to be some validations that you can perform immediately on the client-side without needing to round-trip to the server. Things like comparing the password with the confirmation password, as well as verifying a string matches an email regex can be done with client-side javascript.

For other things like checking whether a username exists or not, you could use an ajax call to sails to directly ask it 'does this username exist' and provide real-time validation on the client-side based on the result, or you can wait until the user submits the form and parse the form submission to display those validations. Since checking ahead of time for things like this aren't 100% reliable (i.e. someone could create a user with that name after the check but prior to the form being posted back), some people choose to forgo the pre-check and only handle the error after post.

Waterline has its own built-in validation mechanism called Anchor, which is built on validator.js (previously called node-validator). For a full list of validations available, see here. I would recommend that instead of defining a separate validation layer, you define a method that parses the sails validation messages and formats them in a way that is user-friendly and consistent.

If you want to perform your own validations outside of what Waterline would do for you, you could do those validations inside a lifecycle callback, for instance the beforeCreate(values, callback) lifecycle callback. If you detect errors, you could pass them into the callback as the first parameter, and they would be passed back as an error to the caller of the create collection method.

An alternative to using a lifecycle callback, would be to create your own collection method that handles the create. Something like this:

Users.validateAndCreate(req.params.all(), function (err, user) {
    ...
});

More information about how to create a collection method like this can be found in my answer to this question: How can I write sails function on to use in Controller?

OTHER TIPS

You can do this in your model:

module.exports = {
types: {
    mycustomtype: function (password) {
        return password === this.confirm;
    }
},
attributes: {,
    password:{
        type: 'STRING',
        required: true,
        mycustomtype: true
    }
}
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top