Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sequelize validation throwing error

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');
                        }
                    });
            }
        }
    },
like image 797
callumacrae Avatar asked Feb 14 '23 06:02

callumacrae


2 Answers

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 :)

like image 80
Jan Aagaard Meier Avatar answered Feb 16 '23 10:02

Jan Aagaard Meier


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.

like image 44
Ben Polge Avatar answered Feb 16 '23 10:02

Ben Polge