Choosing an ORM for node ? (Sequelize / Waterline / Bookshelf)

Posted on: 2018-03-11

Having tested those three ORMs recently, here are the elements that helped me made my choice. Hope it will help you too 😉.

Migrations / Tables creation

  • Waterline: No built-in migration system. Tables are created automaticaly. There 2 settings though: one where the tables are created based on the model's definitions (the tables are re-created when the models change), the second (called "safe") where nothing is done automatically. This is the mode you want to use in production.
  • Sequelize : It has a built-in migration system (up, down, create...). There is also a mode where tables are created automatically but it's safer than waterline's: tables are created only if they do not exist (you can force sequelize to re-create tables when your models change, therefore making it work like waterline).
  • Bookshelf : Bookshelf is based on the knex query builder so if you want to create migrations for your models you can just use knex. Its a great library, but you will have to write the migration manually. Better than waterline but more time consuming than Sequelize.

✅ Sequelize wins

Models definitions

For waterline, we have something like :

const project = {

  tableName : `project`,

  // ...

  attributes : {
    id : {
      type: `string`,
      uuidv4      : true,
      primaryKey: true,
      defaultsTo  : () => uuid.v4()

    // ...

    team : {
      model : `team`,
      required : true

For Sequelize :

const Project = sequelize.define(`project`, {
    uid : {
      type: Sequelize.UUID,
      primaryKey: true,
      defaultValue: Sequelize.UUIDV4,
  }, {
    // ...


And for Bookshelf, you don't have to list the attributes and their types :

const Project = bookshelf.Model.extend({
  tableName: 'project',
  team () {
    return this.belongsTo(Team, `team_id`, `id`);

Bookshelf seems to win on that point, the model declaration is short and straightforward. Sequelize is the most verbose.

✅ Bookshelf wins


With Waterline, we can defin joins pretty easily :

waterline.collections.project.find({ name : 'my project'}).populate('team');

But we can not retrieve nested relations, wich is possible with Sequelize :

  where : { name : 'my project' }
  include : [
      model : Team,
      as : 'team',
      include : [
          model : User,
          as : 'users',

It seems to be possible with Bookshelf also (though I have to admit I didn't try personnaly) :

Project.fetch({withRelated: ['team.users'])

✅ Bookshelf wins (Sequelize close second)


Check out this little test I made on github. Seems waterline is really behind Bookshelf and Sequelize in terms of performances.

✅ Bookshelf and Sequelize win


I would definitely not choose Waterline but both Sequelize and Bookshelf seem to be good choices. Sequelize is a bit more verbose but also it's older and might be more mature. Bookshelf is simpler yet still very powerfull and uses knex, so that means it may also be easy for some critical part of your application to build you own queries.

Anyway, writing this article also convinced me that sometimes, when you are are really concerned about perfomances and having control on what you are doing, a good query builder like knex instead of an ORM is really something to consider. 🤔

Wait for a function if it returns a promise (but you're not sure it does)

Posted on: 2018-02-17
Categories: javascript - es6

If you are working on an npm module, a library or a plugin system, there is times where might want to call a function without being sur it returns a promise or not. Here is an example that may help you achieve what you want :

const returnsAPromise = () => Promise.resolve();
const returnsARejectedPromise = () => Promise.reject(`Error !`);
const returnsOne = () => 1;

const asyncFunction = async (unknwnFunction) => {

  await returnsAPromise();
  // obvioulsy that passes

  try { await returnsARejectedPromise() }
  catch (e) {
    // obviously that fails...
    console.log(`Of course that failed :`, e);

  // now...
  const one = await returnsOne();
  // thats fine and one === 1
  console.log(`I waited a regular function, one = ${one}`);

  // So it means that if we dont know if a function returns a promise
  // or just a value
  // we can just
  const result = await unknwnFunction();
  // it is usefull when you are, for example,
  // programmig an npm module and the function you call is
  // provided by the user
  console.log(`unknownFunction returned (wrapped in a promise or not): ${result}`);


const regularFunction = (unknwnFunction) => {

  // if we are not in a async function, we can also do :
  return Promise.resolve(unknwnFunction())
  .then(result => {
    console.log(`whatever unknownFunction returned : `, result);
  .then(() => {
    // and yes, it would work even if the function fails :
    return Promise.resolve(returnsARejectedPromise());
  .catch(err => {
    // this code is executed
    console.error(`It failed :`, err);


asyncFunction(() => `Hi there !`)
.then(() => regularFunction(() => `Hello ladies !`))

Mongoose : save nested object on instance

Posted on: 2017-11-26
Categories: databases - ORM

Say you have the following schema in mongoose :

const schema = new mongoose.Schema({
  name: String,
  price : Number,
  features : Object

If you have created an instance from model and you try to update it, be aware of this :

const product = await Product.findOne({ name : "Fidget Spinner" });

product.features.coolColor = true;
await; // changes on features object wont be saved !

// you have to tell mongoose to save the changes :
// and then save :
await; // changes on features object are saved :-)

This is because mongoose can't detect changes on an mixed type attribute.