Sequelize models association
.hasMany called with something that’s not a subclass of Sequelize.Model

Recently while working on a NodeJS+Express project I faced this error:
.hasMany called with something that’s not a subclass of Sequelize.Model
But first a little context, I had like 8 to 10 database tables, mostly with one to many relationships. According to Sequelize official documentation, there are 2 ways to implement this relationship: .hasMany() and belongsTo(). The classical example being having a Team with Players it led go something like this:
Team.hasMany(Player);
Player.belongsTo(Team);
However, if you are creating/defining your model this way:
//teamModel.js
const { DataTypes } = require(‘sequelize’);
const sequelize = require(‘../config/database’);
const Team = sequelize.define(‘Team’, {fields…}, {
tableName: ‘Team’,
freezeTableName: true,
timestamps: false
});
Team.hasMany(Player);
module.exports = Team;
and
//playerModel.js
const { DataTypes } = require(‘sequelize’);
const sequelize = require(‘../config/database’);
const Player = sequelize.define(‘Player’, {fields…}, {
tableName: ‘Player’,
freezeTableName: true,
timestamps: false
});
Player.belongsTo(Team);
module.exports = Player;
Doing this will more than likely throw the error mentioned above, first of all we are creating a circular reference by defining this relationship on both models.
If we simple leave this relationship in one of the models, only using for example belongsTo, we won’t get an error. But the downside of this approach is that if you require to use a complex query like a join, and you first use the model that doesn’t have the relationship defined you will then get an error stating that those models are not related, since the order you define your relationships is relevant.
In my case the least restrictive approach that also allows me to define very detailed relationships for every model was mentioned here. But I’ll explain it here in any case:
//team.js
module.exports = (sequelize, DataTypes) => {
const Team = sequelize.define(‘Team’, {fields…}, {});
Team.associate = function(models) {
Team.hasMany(models.Player);
};
return Team;
};
and adding this file:
//index.js
‘use strict’;
var fs = require(‘fs’);
var path = require(‘path’);
var Sequelize = require(‘sequelize’);
var basename = path.basename(module.filename);
var env = process.env.NODE_ENV || ‘development’;
var config = require(__dirname + ‘/../config/server-config.json’)[env];
var db = {};
if (config.use_env_variable) {
var sequelize = new Sequelize(process.env[config.use_env_variable]);
} else {
var sequelize = new Sequelize(config.database, config.username, config.password, config);
}
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;
});
Object.keys(db).forEach(function(modelName) {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
db.sequelize = sequelize;
db.Sequelize = Sequelize;
module.exports = db;
So what is happening in this last file, this one is in charge of reading all the files in the same folder and import them as a sequelize model and add it to our db object. This way worked for me and allowed me to define my relationships more freely.
Also, this line:
var model = sequelize[‘import’](path.join(__dirname, file));
Actually throw an error, apparently Sequelize didn’t have import defined, changing it to Node’s built in CommonJS require function did the trick.
const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes)
I got this solution from David Kamer’s response to this Stack Overflow question.
Hopes this can helps someone and that at the very least helps me fix this issue faster in the future. Thanks for reading!!!