Question

I'm trying to check that a username is unique, and I gather that I'd need a custom validation for that. I've written the following code, but instead of returning the error in the array returned by .validate(), it just throws the error, which isn't the behaviour described in the docs and isn't what I want.

var User = sequelize.define('User', {
    username: {
        type: DataTypes.STRING,
        validate: {
            isUnique: function (username) {
                User.find({ where: { username: username }})
                    .done(function (err, user) {
                        if (err) {
                            throw err;
                        }

                        if (user) {
                            throw new Error('Username already in use');
                        }
                    });
            }
        }
    },
Was it helpful?

Solution

The validation you are doing is asynchronous. You call the User.find method, and after that the validation method returns. Sequelize has no way of knowing that you are doing something async in your validation, unless you tell it so. Once your find call is done you throw the error, but the validation has completed, so there is no code to catch that error, which means the error is thrown and crashes the app

The way to tell sequelize that you are doing something async is to take a second argument to your function, which is a callback. If you call the callback without any arguments, the validation succeeded, if you give it an argument, the validation failed.

var User = sequelize.define('User', {
username: {
    type: DataTypes.STRING,
    validate: {
        isUnique: function (username, done) {
            User.find({ where: { username: username }})
                .done(function (err, user) {
                    if (err) {
                        done(err);
                    }

                    if (user) {
                        done(new Error('Username already in use'));
                    }

                    done();
                });
        }
    }
},

Could you point me to the part of the documentation that mislead you, then we can hopefully correct it :)

OTHER TIPS

Sequelize supports Promise style async operations. Specifically the Bluebird.js lib. Just change your function to specifically use the Promise -> next() pattern.

var User = sequelize.define('User', {
   username: {
    type: DataTypes.STRING,
    validate: {
        isUnique: function (username) {
            User.find({ where: { username: username }})
                .then(function (user) {
                    if (user) {
                        throw new Error('Username already in use');
                    }
                });
        }
    }
},

This will also handle any errors on User.find() for you. See this example in their codebase

Of course the easiest way to handle unique constraints is by setting unique: true on the field definition itself:

var User = sequelize.define('User', {
  username: {
    type: DataTypes.STRING,
    unique: true
  },

But this requires that you are either creating the table using Sequelize or already have a table with the unique constraint set.

This question got a valid answer which explains how the validation option works. But in this situation you don't have to check if a user exists on your own.

Sequelizejs has a findOrCreate() method, that checks if somethings exists before doing anything.

Very useful method :)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top