Sequelize Instance Methods Not Working

I am trying to use Sequelize's instance method to validate a password on login attempt. I have defined the User model as :

var User = sequelize.define('User',{
    id:{
          type:DataTypes.BIGINT,
          autoIncrement: true,
          allowNull: false,
          primaryKey:true
        },
    username:{
          type:DataTypes.STRING,
          unique:true
        },
    password:{
          type: DataTypes.STRING
        },
    ...
  },
  {
    classMethods:{
        associate:function(models){
        ...
        }
      }
  },
  {
    instanceMethods:{
        validatePassword:function(password){
          return bcrypt.compareSync(password, this.password);
        }
      }
  }
);
  return User;
}

In my login route I do the following :

  • 1) Retrieve username & password from request body
  • 2) Check if username exists in database
  • 3) If user exists, get user object and compare sent password with hashed password in database using validatePassword method.

Here is the relevant code

var username = req.body.username || "";
var password = req.body.password || "";
models.User.findOne({ where: {username: username} }).
then(
    function(user) {
     if(user){
      console.log(user.validatePassword(password));
     }
 ....

Each time I try to login I get the following error

[TypeError: user.validatePassword is not a function]

What am I doing wrong?

2

5 Answers

I think you are using the sequelize model definition api incorrectly.

This is the correct way:

var User = sequelize.define('User',{}, {
  classMethods: {
    method1: ...
  },
  instanceMethods: {
    method2: ...
  }
});

not like this:

var User = sequelize.define('User',{}, {
  classMethods: {
    method1: ...
  }
},{
  instanceMethods: {
    method2: ...
  }
});
1

For anyone who's having a similar problem, I ran into the same issue but using Sequelize 5.21.5. According to this article, Sequelize Instance Methods, starting with Sequelize 4.0 and above, you have to use the prototype methodology in order to define instance methods like so:

    // Adding an instance level methods.
    User.prototype.validPassword = function(password) {
      return bcrypt.compareSync(password, this.password);
};
2

We can add instanceLevelMethods to prototype,

User.prototype.your-instance-level-method-name = function() {
    return 'foo';
};

I did it like this:

// Adding an instance level methods.
User.prototype.validPassword = function(password) {
    return bcrypt.compareSync(password, this.password);
};
1

as at sequelize "sequelize": "^5.21.7" accessing the instanceMethods as shown by @user1695032 returns undefined.

here's what i found after several hours of getting undefined in the console.log() passing in the user object return from the query below:

User {
  dataValues: {
    id: 1,
    firtName: null,
    lasteName: null,
    email: '',
    phone: null,
    password: '$2b$10$yEWnBFMAe15RLLgyU3XlrOUyw19c4PCmh8GJe9QVz3YkbdzK5fHWu',
    createdAt: 2020-05-27T21:45:02.000Z,
    updatedAt: 2020-05-27T21:45:02.000Z
  },
  _previousDataValues: {
    id: 1,
    firtName: null,
    lasteName: null,
    email: '',
    phone: null,
    password: '$2b$10$yEWnBFMAe15RLLgyU3XlrOUyw19c4PCmh8GJe9QVz3YkbdzK5fHWu',
    createdAt: 2020-05-27T21:45:02.000Z,
    updatedAt: 2020-05-27T21:45:02.000Z
  },
  _changed: {},
  **_modelOptions: {**
    timestamps: true,
    validate: {},
    freezeTableName: false,
    underscored: false,
    paranoid: false,
    rejectOnEmpty: false,
    whereCollection: { email: '' },
    schema: null,
    schemaDelimiter: '',
    defaultScope: {},
    scopes: {},
    indexes: [],
    name: { plural: 'Users', singular: 'User' },
    omitNull: false,
    **instanceMethods: { comparePasswords: [Function: comparePasswords] },**
    hooks: { beforeValidate: [Array] },
    sequelize: Sequelize {
      options: [Object],
      config: [Object],
      dialect: [MysqlDialect],
      queryInterface: [QueryInterface],
      models: [Object],
      modelManager: [ModelManager],
      connectionManager: [ConnectionManager],
      importCache: [Object]
    }
  },
  _options: {
    isNewRecord: false,
    _schema: null,
    _schemaDelimiter: '',
    raw: true,
    attributes: [
      'id',        'firtName',
      'lasteName', 'email',
      'phone',     'password',
      'createdAt', 'updatedAt'
    ]
  },
  isNewRecord: false
}

the code before the error:

 models.User.findOne({where: {email: req.body.email}}).then((user)=>{
            console.log(user)
            if(!user) {
                res.status(401).json({ message: 'Authentication failed!' });
                } else {
                user.comparePasswords(req.body.password, (error, isMatch) =>{
                    console.log(error + ' -- ' + isMatch)
                    if(isMatch && !error) {
                        const token = jwt.sign(
                            { username: user.username },
                            keys.secret,
                            { expiresIn: '30h' }
                        );

                        res.status(200).json({ success: true,message: 'signed in successfully', token: 'JWT ' + token });
                    } else {
                        res.status(401).json({ success: false, message: 'Login failed!' });
                    }
                });
            }
        }).catch((error)=>{
            console.log(error)
            res.status(500).json({ success: false, message: 'There was an error!'});
        })

this cause TypeError: user.comparePasswords is not a function

after changing this line:

** user.comparePasswords(req.body.password, (error, isMatch) =>{} **

to this:

** user._modelOptions.instanceMethods.comparePasswords(req.body.password, (error, isMatch) =>{}**

booooom! everything worked

I use this approach:

const bcrypt = require('bcrypt-nodejs');

const constants = require('../constants/users');

module.exports = (Sequelize, type) => {
  const User = Sequelize.define(constants.TABLE_NAME, {
    username: {
      type: type.STRING,
      unique: true,
      allowNull: false,
    },
    password: {
      type: type.STRING,
      allowNull: false,
    },
    // bla bla
  });

  const setSaltAndPassword = async function(user) {
    if (user.changed('password')) {
      const salt = bcrypt.genSaltSync(constants.PASSWORD_SALT_SIZE);
      user.password = bcrypt.hashSync(user.password, salt);
    }
  };

  User.prototype.validPassword = async function(password) {
    return await bcrypt.compare(password, this.password);
  };

  User.beforeCreate(setSaltAndPassword);
  User.beforeUpdate(setSaltAndPassword);

  return User;
};
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Chloe Bennett

Chloe Bennett

Culture, Media & Entertainment Columnist

Chloe Bennett explores the intersection of pop culture, streaming entertainment, digital trends, and contemporary lifestyle. Her weekly commentary reaches thousands of culture enthusiasts.

Share this article
Twitter Facebook Pinterest