Sequelize Migrations — Setting Up Associations
This post will cover creating associations with migrations. I will go over how to set up associations in the migration file s— which sets up associations in your database, as well as in the corresponding model files — which sets up associations in Sequelize itself.

So what is Sequelize, and how do you use migrations? Rather than re-invent the wheel, if you are not familiar with Sequelize or migrations, I’m going to refer you to this fantastic article written by Dean Slama Jr (6 minute read) which will provide you a basic overview. I am going to dive a little deeper into setting up associations when creating your database via migrations. Dean’s article is a great overview, and I’m going to quickly explain why its a good idea to use migrations to set up your database for those who are not familiar with the other way of setting up a database with Sequelize.
Why use migrations?
Note: it seems to be convention to pluralize model definitions in migrations files and singularize definitions in model files.
I originally learned to create a database with Sequelize as described in the Getting Started section of the docs. Based on how the docs were structured at the time, I thought this was the right way to do things. You define models via the sequelize.define
method, and then sync the models to the database via the sequelize.sync
method. Below is a barebones example adapted from the Sequelize docs:
// in your db.js file, initiate the sequelize instance and connect to your postgres DB
var Sequelize = require('sequelize');
var sequelize = new Sequelize('postgres://user:pass@example.com:5432/dbname');//In user.js, require your db, and define a model
var User = db.define('user', {
firstName: {
type: Sequelize.STRING
},
lastName: {
type: Sequelize.STRING
}
});/* require your db in you server.js, and sync it
*{force: true} option will drop the table if it already exists and recreate it.
*/
db.sync({force: true});
I would set up my development/testing database this way, and have a seeder file to repopulate the database with data that would dropped upon Model.sync({force: true})
. Forcing the database to drop all the tables allows you to make changes to a model after already having defined and synced it to the database. I think we all know this is NOT safe way to do things for a production database, and this is precisely why its a good idea to set up your database using migrations and avoid using the methodology above (unless you don’t mind if all the data in your database gets wiped out). Using migrations allows you to easily and safely update your tables and database. For example, If you want add a age
column on the users
table you can create a new migration, and include the following:
up: function (queryInterface, Sequelize) {
return queryInterface.addColumn(
'users',
'age',
Sequelize.INTEGER
);
},down: function (queryInterface, Sequelize) {
return queryInterface.removeColumn('users', 'age');
}
Then, just add the column into your model file. You must setup your database with migrations in order to be able to use migrations with your database (its a major hassle to switch over otherwise), so PLEASE do so.
You should set up model files that match your migrations
Use sequelize model:create ‘name' — attributes:’attr:string, attr2:integer'
to create both a migration file and model file.
The migration files will create your database/adjust your tables when you run db:migrate
, but you should set up your model file to match your initial migration and any later changes! The migrations will set associations in the postgres database itself, but NOT in Sequelize. Lets say you’ve set up a user and order model with migrations and have created the proper association. If you try to find a user and use include
to grab all orders associated with the user, you will get an error message stating Error: Association with alias “orders” does not exists
.
return User.findOne({ where: { id: req.params.id }, include: ['order'] })
.then(handleResponse(res), handleError(res));
});
After running sequelize init
, the index.js was created in the models folder, and it connects Sequelize with the database. The following lines of index.js are why matching model files are needed when it comes to associations:
models/index.js...
var db = {};
...//grabs all the models in your models folder, adds them to the db object
fs
.readdirSync(__dirname)
.filter(function(file) {
return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
})
.forEach(function(file) {
var model = sequelize['import'](path.join(__dirname, file));
db[model.name] = model;
});//Important: creates associations based on associations defined in associate function in the model files
Object.keys(db).forEach(function(modelName) {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
Associations are established in Sequelize based on the associate function defined on your model class in the model files; so be sure to carefully define all associations there too. Besides setting up associations, you can add features like instance methods, hooks, etc in your model files.
Setting up associations — migration and model files
IMPORTANT: as of 6/23/17 the model file created with the sequelize db:model
command still initializes a model with an empty classMethods
object with an associate
property in the options passed to sequelize.define
method. You cannot define associations this way anymore as of Sequelize v4.0.0-1. This tripped me up for a hot second because the generated model file did not reflect this change, and the fact that support for the old way had been removed is seemingly buried in the changelogs. Don’t make the same mistake I did and be super confused for too long about why associations aren’t working.
//old way that will be included in your generated model file
module.exports = function(sequelize, DataTypes) {
var nameofmodel = sequelize.define('nameofmodel', {
...model attributes
}, {
classMethods: {
associate: function(models) {
// associations can be defined here
}
}
});
return nameofmodel;
};//The right way to set associations in model files
module.exports = function(sequelize, DataTypes) {
var nameofmodel = sequelize.define('nameofmodel', {
...model attributes
});
nameofmodel.associate = function (models) {
// associations can be defined here
}; return nameofmodel;
};
Migration File for one-to-one or one-to-many association:
Setting up associations in your migration file is an simple process. If you need to add a foreign key in the case of a one-to-many or one-to-one relationship, include the following with the attribute definition in the migration file:
migrations/model1.jsmodel2Id: {
type: Sequelize.INTEGER,
references: {
model: 'model2',
key: 'id'
},
},
Model File for one-to-one or one-to-many association:
models/model1.jsmodel1.associate = function (models) {
model1.belongsTo(models.model2);
};
But what if you need to set up a join table (belongsToMany
)? You will have to setup a migration file and manually create the join table, and then set your corresponding associations in the models folder. Let’s say you’re building a super simple shopping app and will need a products
and orders
model and that will have a many-to-many relationship; a order can have many products, and a product can be in many orders. You will create a migration file to setup a model called product_orders
:
Migration File for many-to-many association:
migrations/project_order.js//Note: I am oversimplifying this model For the sake of brevity
module.exports = {
up: function (queryInterface, Sequelize) {
return queryInterface.createTable('product_orders',
{
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
productId: {
type: Sequelize.INTEGER,
references: {
model: 'products',
key: 'id'
},
allowNull: false
},
orderId: {
type: Sequelize.INTEGER,
references: {
model: 'orders',
key: 'id'
},
allowNull: false
}
createdAt: Sequelize.DATE,
updatedAt: Sequelize.DATE,
}
)},down: function (queryInterface, Sequelize) {
return queryInterface.dropTable('product_orders')
}
};
Model File for many-to-many association:
models/order.jsorder.associate = function (models) {
order.belongsToMany(models.product, { through: 'product_order', as: 'product' });
};models/product.js
product.associate = function (models) {
product.belongsToMany(models.order, { through: 'product_order', as: 'order' });
};
the as: ‘aliasName'
is will create an alias that you can pass to include when querying for your orders/product. You will also have a product_order.js model file so it gets added to the Sequelize instance in models/index.js. Here is an example of how you can use this association with orders:
var db = '../models'
var Order = db.order;
var Product = db.product;app.route('/order/:id')
.get(function(req, res) {
return Order.findOne({ where: { id: req.params.id }, include: ['product'] })
.then(handleResponse(res), handleError(res));
})
.put(function(req, res) {
return Order.findOne({ where: { id: req.params.id } })
.then(function(order) {
return order.addProduct(req.body.productId);
})
.then(handleResponse(res), handleError(res));
});
I hope this was helpful and thanks for reading! Let me know if you think I should change any of this information.
Here is another good example resource for migrations/associations:
https://gist.github.com/JoeKarlsson/ebb1c714466ae3de88ae565fa9ba4779
And here is my example:
Here is another great resource:
https://www.duringthedrive.com/2017/05/06/models-migrations-sequelize-node/