5

So i was implementing a bit of change in my server application - switching databases from MongoDb to PostgreSQL (with Sequelize 6) and i had to change the controller functions i had created for mongoose to suit the current database but there was a problem implementing the instance methods, but as usual there was little to no helpful solutions online for this with Sequelize 6. But now there is. Below are the code samples for some of the problems and error messages you may be facing if you come across this post.

The function which calls the instance method: userController.js (login function)

User.findByPk(req.body.id)
    .then(user => {
        if (!user) {
            return res.status(401).send('Sorry!! You do not have an account with us.')
        }

        if (!user.validPassword(req.body.password)) {
            return res.status(401).send('Invalid Password')
        } else {
            res.status(200).json({ user })
        }
    })
    .catch(error => {
        console.log(error)
        res.status(500).send({
            message: 'Some error occurred while logging in this User',
            error: error.message
        });
    });

EXAMPLE CODE 1

user.js

'use strict';
const { Model } = require('sequelize');

module.exports = (sequelize, DataTypes) => {
    class User extends Model {
        /**
         * Helper method for defining associations.
         * This method is not a part of Sequelize lifecycle.
         * The `models/index` file will call this method automatically.
         */
        static associate(models) {
            // define association here
        }
    };

    User.init({
        username: {
            type: DataTypes.STRING,
            allowNull: false,
            unique: true
        },
        password: {
            type: DataTypes.STRING,
            allowNull: false,
            unique: true
        },
        password_confirmation: {
            type: DataTypes.STRING,
            allowNull: false,
            unique: true
        }
    }, {
        hooks: {
            beforeCreate: (User) => {
                const salt = bcrypt.genSaltSync();
                User.password = bcrypt.hashSync(User.password, salt);
                User.password_confirmation = User.password;
            }
        },
        instanceMethods: {
            validatePassword: (password) => {
                return bcrypt.compareSync(password, this.password);
            }
        }
        sequelize,
        modelName: 'User',
    });
    return User;
};

response (Server 500 error message)

{
    "message": "Some error occurred while logging in this User",
    "error": "user.validPassword is not a function"
}

The instance method in this case is not recognized and the function validPassword() is not run thus a 500 Server error. Let's move to example 2.

EXAMPLE CODE 2

user.js

'use strict';
const { Model } = require('sequelize');

module.exports = (sequelize, DataTypes) => {
    class User extends Model {
        /**
         * Helper method for defining associations.
         * This method is not a part of Sequelize lifecycle.
         * The `models/index` file will call this method automatically.
         */
        static associate(models) {
            // define association here
        }
    };

    User.init({
        username: {
            type: DataTypes.STRING,
            allowNull: false,
            unique: true
        },
        password: {
            type: DataTypes.STRING,
            allowNull: false,
            unique: true
        },
        password_confirmation: {
            type: DataTypes.STRING,
            allowNull: false,
            unique: true
        }
    }, {
        hooks: {
            beforeCreate: (User) => {
                const salt = bcrypt.genSaltSync();
                User.password = bcrypt.hashSync(User.password, salt);
                User.password_confirmation = User.password;
            }
        },
        instanceMethods: {
            validatePassword: (password) => {
                return bcrypt.compareSync(password, this.password);
            }
        }
        sequelize,
        modelName: 'User',
    });

    User.prototype.validPassword = (password) => {
        return bcrypt.compareSync(password, this.password);
    };

    return User;
};

response (Server 500 error message)

{
    "message": "Some error occurred while logging in this User",
    "error": "Illegal arguments: string, undefined"
}

The instance method in this case is still not recognized and the function validPassword() is thus not run because over here the parameter this.password for the bcrypt.compareSync() function is not defined (or has been used outside the Model extension) thus a 500 Server error.

And now for the solution.

3 Answers 3

9

After around half a day of searching, I found out that for some reason the instanceMethods functionality has been removed in Sequelize v4. As a result, the only way to obtain this functionality is by one of the following:

  • declaring the function on the model class as Jeffrey Dabo suggested
  • adding the function on the prototype of the Sequelize model

Very important: If you go with the prototype approach, in order to have access to the this object, you need to declare the function using the function syntax and not as an arrow function, or else it will not work.

Example:

const User = sequelize.define('User', {...});

User.prototype.validatePassword = function (password) {
  return bcrypt.compareSync(password, this.password);
}
Sign up to request clarification or add additional context in comments.

Comments

1

Placing the instance method function right under the class method function (associations on models) would eventually allow your function validPassword() to be recognized, run and produce the desired response.

user.js

'use strict';
const { Model } = require('sequelize');

module.exports = (sequelize, DataTypes) => {
    class User extends Model {
        /**
         * Helper method for defining associations.
         * This method is not a part of Sequelize lifecycle.
         * The `models/index` file will call this method automatically.
         */
        static associate(models) {
            // define association here
        }

        validPassword(password) => {
            return bcrypt.compareSync(password, this.password);
        };
    };

    User.init({
        username: {
            type: DataTypes.STRING,
            allowNull: false,
            unique: true
        },
        password: {
            type: DataTypes.STRING,
            allowNull: false,
            unique: true
        },
        password_confirmation: {
            type: DataTypes.STRING,
            allowNull: false,
            unique: true
        }
    }, {
        hooks: {
            beforeCreate: (User) => {
                const salt = bcrypt.genSaltSync();
                User.password = bcrypt.hashSync(User.password, salt);
                User.password_confirmation = User.password;
            }
        },
        instanceMethods: {
            validatePassword: (password) => {
                return bcrypt.compareSync(password, this.password);
            }
        }
        sequelize,
        modelName: 'User',
    });

    return User;
};

1 Comment

There is an example of how to create custom instance methods here: sequelize.org/master/manual/… Also there is no options.instanceMethods where you were trying to add your instance method. Reference: sequelize.org/master/class/lib/…
1

I don't think this will help you but I can see that you have a syntax error in your codes, try adding , before

sequelize, modelName: 'User'

 {
    hooks: {
        beforeCreate: (User) => {
            const salt = bcrypt.genSaltSync();
            User.password = bcrypt.hashSync(User.password, salt);
            User.password_confirmation = User.password;
        }
    },
    instanceMethods: {
        validatePassword: (password) => {
            return bcrypt.compareSync(password, this.password);
        }
    },
    sequelize,
    modelName: 'User',
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.