Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pre-save hook and instance methods in Sequelize?

Are there pre-save hooks and instance methods in Sequelize.js?

Specifically I need to convert this Mongoose code into an equivalent Sequelize code:

Schema

var userSchema = new mongoose.Schema({
  username: { type: String, unique: true },
  email: { type: String, unique: true },
  password: String,
  token: String
});

Pre-save

userSchema.pre('save', function(next) {
  var user = this;

  var hashContent = user.username + user.password + Date.now() + Math.random();
  user.token = crypto.createHash('sha1').update(hashContent).digest('hex');

  if (!user.isModified('password')) return next();
  bcrypt.genSalt(5, function(err, salt) {
    if (err) return next(err);
    bcrypt.hash(user.password, salt, function(err, hash) {
      if (err) return next(err);
      user.password = hash;
      next();
    });
  });
});

Instance method

userSchema.methods.comparePassword = function(candidatePassword, cb) {
  bcrypt.compare(candidatePassword, this.password, function(err, isMatch) {
    if(err) return cb(err);
    cb(null, isMatch);
  });
};
like image 541
Sahat Yalkabov Avatar asked Jan 28 '14 16:01

Sahat Yalkabov


2 Answers

The best way is to expand your model with class or instance methods:

var User = sequelize.define('User', {
    username: { type: Sequelize.STRING, unique: true },
    email: { type: Sequelize.STRING, unique: true },
    password: Sequelize.STRING,
    token: Sequelize.STRING
}, {
    instanceMethods: {
        comparePassword : function(candidatePassword, cb) {
            bcrypt.compare(candidatePassword, this.getDataValue('password'), function(err, isMatch) {
                if(err) return cb(err);
                cb(null, isMatch);
            });
        },
        setToken: function(){
            // bla bla bla
            // bla bla bla
        },
        getFullname: function() {
            return [this.firstname, this.lastname].join(' ');
        }
    }
})

So when you do

User.build({ firstname: 'foo', lastname: 'bar' }).getFullname(); // 'foo bar'

So, to set the token, you can do it like this:

User.build({ ... }).setToken().save();

Or, to use the comparePassword function:

User.find({ ... }).success(function(user) { user.comparePassword('the password to check', function(err, isMatch) { ... } });

You can see this in the Sequelize docs

Edit

On recent versions of there are hooks for each model, you can check the hooks documentation which are very pretty straightforward and complement them with the class or instance methods.

like image 187
Dan Rocha Avatar answered Sep 22 '22 03:09

Dan Rocha


I faced same issue, but atleast in 2.0 version of sequelize this feature is available, the complete documentation is available at Hooks.

Below is a sample code that uses a beforeValidate hook:

"use strict";
var md5 = require('blueimp-md5').md5;

module.exports = function(sequelize, DataTypes) {
  var Sms = sequelize.define("sms", {
    senderName: DataTypes.STRING,
    smsBody : {
      type : DataTypes.STRING, allowNull:false
    },
    userId : {
      type: DataTypes.INTEGER, allowNull:false
    },
    hash : {
      type:DataTypes.CHAR(32),
      unique:true,
      allowNull:false
    }

  });

Sms.beforeValidate(function(sms){
  sms.hash = md5(sms.smsBody+sms.userId);
  return sequelize.Promise.resolve(sms)
});


return Sms;
};

The requirement here was, create a hash using smsBody and userId, so i created hook i.e. beforeValidate, this hook will be executed before any validations are performed by Sequelize on the model. There are many other hooks available and best part is you don't have to write any additional code while you save your data, these hooks will take care of that.

You should choose wisely between hooks and instanceMethods. But in your case i guess hooks would be a better choice

like image 25
sunitj Avatar answered Sep 20 '22 03:09

sunitj