State of v1.7.0

Introduction

Since the release of v1.6.0 in April 2013, many new features have been added to Sequelize. This was mostly possible due to the great help of the community. Furthermore Daniel Durante joined us in May, who is a big help everyday and will be glad to help everyone who joins our IRC channel.

This post is about the features, that have been added since April and that are currently available in the current NPM package v1.7.0-alpha2.

[DEPENDENCIES] Upgraded validator for IPv6 support

The validator plugin has been updated to 1.1.1. Part of the new version is the possibility to check for proper IPv6 addresses. You can use it like this:

Sequelize.define('model', {
  ip: {
    type:     Sequelize.STRING,
    validate: {
      isIPv6: true
    }
  }
})

[DEPENDENCIES] replaced underscore by lodash

In order to improve performance of the library, we replaced underscore.js with the faster drop-in-replacement lodash . You can access it via the already existing way Sequelize.Utils._.

[FEATURE] Validate a model before it gets saved

Many people requested, that the model needs to be validated before it gets saved. This is now the case. So whenever the save method gets called on an object, the model is validated and the error callback is triggered if the model isn't valid:

sequelize.define('user', {
  username: {
    type:     Sequelize.STRING,
    validate: {
      notEmpty: true,
      notNull:  true
    }
  }
}).create({
  username: ''
}).error(function(errors) {
  console.log(errors) // { username: [ 'String is empty' ] }
})

[FEATURE] Schematics

It is now possible to create schemas for postgres respectively to prefix table names for sqlite and mysql. That means that you can now define tables in different scopes:

var User   = sequelize.define('user', { username: Sequelize.STRING })
  , wpUser = User.schema('wordpress', '_')
 
sequelize.createSchema('wordpress').success(function() {
  sequelize.sync().success(function() {
    // Sequelize will now create the following tables: `wordpress_users`
  })
})

[FEATURE] Foreign key constraints

It is now possible to define foreign keys to connect tables / models with each other. The following snippet connects the Post model with the Author model.

var Author = sequelize.define('author', { first_name: Sequelize.STRING })
var Post   = sequelize.define('post', {
  title:    Sequelize.STRING,
  authorId: {
    type:          Sequelize.INTEGER,
    references:    Author,
    referencesKey: "id"
  }
})
 
Author.hasMany(Post)
Post.belongsTo(Author)

Also we can now define reactions of certain events. E.g. what should happen to the associated objects after a delete operation, etc. This is the way to do it:

var Author = sequelize.define('author', { first_name: Sequelize.STRING })
var Post   = sequelize.define('post', {
  title: Sequelize.STRING,
  authorId: {
    type:          Sequelize.INTEGER,
    references:    'authors',
    referencesKey: 'id',
    onDelete:      'cascade'
  }
})
 
Author.hasMany(Post)
Post.belongsTo(Author)
 
sequelize.sync().success(function() {
  new Sequelize.Utils.QueryChainer()
    .add(Author.create({ first_name: 'John' }))
    .add(Post.create({ title: 'news' }))
    .add(Post.create({ title: 'milestone reached' }))
    .run()
    .success(function(results) {
      var author    = results[0]
        , news      = results[1]
        , milestone = results[2]
 
      author.setPosts([ news, milestone ]).success(function() {
        author.destroy()
        /*
          At this point, you won't find any authors and posts in the database anymore,
          as we have dropped the only author and due to the cascade option every related
          posts of him.
        */
      })
    })
})

[FEATURE] Support for bulk insert/update/delete

In order to minimize the number of SQL requests while creating/updating/deleting a bunch of entries, we introduced bulk support.

This is how creation of multiple instances is possible while just triggering a single SQL statement:

var User = sequelize.define('User', { name: Sequelize.STRING })
 
User.sync({ force: true }).success(function() {
  var userData = [
    { name: 'John' },
    { name: 'Jane' },
    { name: 'Pete' }
  ]
 
  User.bulkCreate(userData).success(function() {
    // We just created 3 users with just a single SQL statement <3
  })
})

In order to update a bunch of entries we can utilize the update method:

User.update({ name: 'Foo' }, '`name` LIKE "J%"').success(function() {
  // All users thats name used to start with "J" is now called "Foo" :)
})

Last but not least we can delete all entries matching certain criteria:

User.bulkCreate([
  { name: 'John' },
  { name: 'Jane' },
  { name: 'Pete' }
]).success(function() {
  User.destroy('`name` LIKE "J%"').success(function() {
    // We just deleted all rows that have a name starting with "J"
  })
})

[FEATURE] Added convenient data types

Costent added support for advanced data types. For example, you are now able to define a column as unsigned, zerofilled integer having a length of 6:

var User = sequelize.define("user", {
  streetNumber: Sequelize.INTEGER(6).ZEROFILL.UNSIGNED
})

Also we have added BIGINT, DECIMAL, ENUM, ARRAY and some other things. They are nicely documented over here.

[FEATURE] Binary is more verbose now

Rob Fletcher made the Sequelize binary a bit more talkative, when it comes to errors or misusage.

[FEATURE] Promises/A support

In order to make chaining of events easier and smoother, Sequelize now has support for Promises/A. Just call then instead of reacting on the usual events:

var User = sequelize.define("user", {
  username: Sequelize.STRING
})
 
User
  .sync({ force: true })
  .then(function() { return User.create({ username: 'John' }) })
  .then(function(john) { return User.create({ username: 'Jane' }) })
  .then(function(jane) { return User.create({ username: 'Pete' }) })
  .then(function(pete) {
    console.log("we just created 3 users :)")
    console.log("this is pete:")
    console.log(pete.values)
  })

[FEATURE] Added Getters/Setters method for DAO

Virtual attributes have been introduced and allows you, to e.g. set a first/last name based on a name respectively to do the vice versa way:

var User = sequelize.define("user", {
  first_name: Sequelize.STRING,
  last_name:  Sequelize.STRING
}, {
  setterMethods: {
    name: function(s) {
      this.first_name = s.split(" ")[0]
      this.last_name  = s.split(" ")[1]
    }
  },
 
  getterMethods: {
    name: function() {
      return [this.first_name, this.last_name].join(" ")
    }
  }
})
 
User
  .sync({ force: true })
  .then(function() {
    return User.create({ name: 'John Doe' })
  })
  .then(function(john) {
    console.log("Let's welcome:", john.first_name, john.last_name)
    // Let's welcome: John Doe
  })

[FEATURE] Added model wide validations

In addition to the already existing possibilities of validating certain single attributes, the current version adds support for instance wide validations. It utilizes the validate option of the model definition:

var User = sequelize.define("User", {
  username: {
    type: Sequelize.STRING,
    validate: { notEmpty: true }
  },
  password:              Sequelize.STRING,
  password_confirmation: Sequelize.STRING
}, {
  validate: {
    passwordEquality: function() {
      if (this.password !== this.password_confirmation) {
        throw new Error("Passwords aren't equal!")
      }
    }
  }
})
 
User
  .sync({ force: true })
  .then(function() {
    User.create({
      username:              'john',
      password:              'test',
      password_confirmation: 'tst'
    }).error(function(err) {
      console.log(err)
      // { passwordEquality: [ 'Passwords aren\'t equal!' ] }
    })
  })

Bug-Fixes

  • [BUG] Fix string escape with postgresql on raw SQL queries. #586
  • [BUG] "order by" is now after "group by". #585
  • [BUG] Added decimal support for min/max. #583
  • [BUG] Null dates don't break SQLite anymore. #572
  • [BUG] Correctly handle booleans in MySQL. #608
  • [BUG] Fixed empty where conditions in MySQL. #619
  • [BUG] Allow overriding of default columns. #635
© Sascha Depold, et al. 2006 - 2022