Sequelize 6 import models from file

I would like to know how can I import the models from file with Sequelize 6 ?

It works with "sequelize": "^5.22.0",
"sequelize-cli": "^5.5.1", but I have an error with Sequelize 6.

Currently, I have this :

database/setup/databaseConnection.js

// Imports
import { Sequelize } from "sequelize"

const connection = new Sequelize(
    process.env.DATABASE_NAME,
    process.env.DATABASE_USER,
    process.env.DATABASE_PASSWORD,
    {
        host: process.env.DATABASE_URL,
        port: process.env.DATABASE_PORT,
        dialect: "mysql",
        logging: false,
        define: {
            // prevent sequelize from pluralizing table names
            freezeTableName: true,
        },
    }
)

// Test connection
console.info("SETUP - Connecting database...")

connection
    .authenticate()
    .then(() => {
        console.info("INFO - Database connected.")
    })
    .catch((err) => {
        console.error("ERROR - Unable to connect to the database:", err)
    })

export { connection as default }

database/models/index.js

// Imports
import Sequelize from "sequelize"

// App Imports
import connection from "../setup/databaseConnection"

const models = {
    Language: connection.import("./language"),
}

Object.keys(models).forEach((modelName) => {
    if ("associate" in models[modelName]) {
        models[modelName].associate(models)
    }
})

models.sequelize = connection
models.Sequelize = Sequelize

export { models as default }

database/models/language.js

module.exports = (sequelize, DataTypes) => {
    const Language = sequelize.define(
        "language",
        {
            /* id       : {
         primaryKey: true,
         type      : DataTypes.INTEGER
         }, */
            name: {
                type: DataTypes.STRING,
            },
            code: {
                type: DataTypes.STRING,
            },
            is_active: {
                type: DataTypes.BOOLEAN,
            },
        },
        {}
    )
    Language.associate = function (models) {
        // Language has Many Bucket
        models.Language.hasMany(models.Bucket, {
            foreignKey: "id",
        })
    }
    return Language
}

But I have this error :

formation-api/database/models/index.js:17
  Language: _databaseConnection["default"]["import"]("./language")
                                                    ^

TypeError: _databaseConnection.default.import is not a function
    at Object.<anonymous> (/Users/jeremiechazelle/Sites/api/database/models/index.js:8:15)
    at Module._compile (internal/modules/cjs/loader.js:1147:30)
    at Module._compile (/Users/jeremiechazelle/Sites/api/node_modules/pirates/lib/index.js:99:24)
    at Module._extensions..js (internal/modules/cjs/loader.js:1167:10)
    at Object.newLoader [as .js] (/Users/jeremiechazelle/Sites/api/node_modules/pirates/lib/index.js:104:7)
    at Module.load (internal/modules/cjs/loader.js:996:32)
    at Function.Module._load (internal/modules/cjs/loader.js:896:14)
    at Module.require (internal/modules/cjs/loader.js:1036:19)
    at require (internal/modules/cjs/helpers.js:72:18)
    at Object.<anonymous> (/Users/jeremiechazelle/Sites/api/resolvers/Queries/User.js:2:1)
[nodemon] app crashed - waiting for file changes before starting...

I use :
"sequelize": "^6.1.0",
"sequelize-cli": "^6.0.0"

Here is Solutions:

We have many solutions to this problem, But we recommend you to use the first solution because it is tested & true solution that will 100% work for you.

Solution 1

Sequelize ^6.x not longer support sequelize.import. They say that you should use require instead.

I understand that you have to import models manually.

Here is an example using require

./models/index.js

const dotenv = require('dotenv');
const fs = require('fs');
const path = require('path');
const { Sequelize, DataTypes } = require('sequelize');

const filebasename = path.basename(__filename);
const db = {};

// Get env var from .env
dotenv.config()
const { DB_HOST, DB_USER, DB_PASS, DB_NAME, DB_FORCE_RESTART } = process.env;

const config = {
  host: DB_HOST,
  dialect: 'mysql',
  dialectOptions: {
    charset: 'utf8',
  }
}

const sequelize = new Sequelize(DB_NAME, DB_USER, DB_PASS, config);

fs
  .readdirSync(__dirname)
  .filter((file) => {
    const returnFile = (file.indexOf('.') !== 0)
      && (file !== filebasename)
      && (file.slice(-3) === '.js');
    return returnFile;
  })
  .forEach((file) => {
    const model = require(path.join(__dirname, file))(sequelize, DataTypes)
    db[model.name] = model;
  });


Object.keys(db).forEach((modelName) => {
  if (db[modelName].associate) {
    db[modelName].associate(db);
  }
});

db.sequelize = sequelize;
db.Sequelize = Sequelize;

const sequelizeOptions = { logging: console.log, };

// Removes all tables and recreates them (only available if env is not in production)
if (DB_FORCE_RESTART === 'true' && process.env.ENV !== 'production') {
  sequelizeOptions.force = true;
}

sequelize.sync(sequelizeOptions)
  .catch((err) => {
    console.log(err);
    process.exit();
  });

module.exports = db;

./models/User.js

'use strict';

import cryp from 'crypto';

module.exports = function (sequelize, DataTypes) {
  const User = sequelize.define('User', {
    email: {
      type: DataTypes.STRING(50),
      allowNull: false,
      unique: true,
      validate: {
        isEmail: { msg: "Please enter a valid email addresss" }
      },
      isEmail: true
    },
    password_hash: { type: DataTypes.STRING(80), allowNull: false },
    password: {
      type: DataTypes.VIRTUAL,
      set: function (val) {
        //this.setDataValue('password', val); // Remember to set the data value, otherwise it won't be validated
        this.setDataValue('password_hash', cryp.createHash("md5").update(val).digest("hex"));
      },
      validate: {
        isLongEnough: function (val) {
          if (val.length < 8) {
            throw new Error("Please choose a longer password");
          }
        }
      }
    },
    role: {
      type: DataTypes.INTEGER,
      allowNull: false
    },
    active: {
      type: DataTypes.INTEGER,
      allowNull: false,
      defaultValue: 1
    }

  }, {
    classMethods: {
      associate: function (models) {
        // User.belongsTo(models.Department, { foreignKey: { allowNull: false } });
        // User.belongsTo(models.Position, { foreignKey: { allowNull: false } });
        // User.belongsTo(models.Profile, { foreignKey: { allowNull: false } });

        // User.hasMany(models.Report, { foreignKey: { allowNull: false } });
        // User.hasMany(models.Notification, { foreignKey: { allowNull: false } });
        // User.hasMany(models.Response, { foreignKey: { allowNull: false } });

      }
    },

    timestamps: true,

    // don't delete database entries but set the newly added attribute deletedAt
    // to the current date (when deletion was done). paranoid will only work if
    // timestamps are enabled
    paranoid: false,

    // don't use camelcase for automatically added attributes but underscore style
    // so updatedAt will be updated_at
    underscored: true
  });
  return User;
};

.env

DB_HOST=localhost
DB_USER=wilo087
DB_PASS=temp
DB_NAME=database_name
DB_FORCE_RESTART=true #Remove and create tables

Full example on:
https://github.com/wilo087/pethome_raffle_backend/tree/develop

Solution 2

like this:

import Sequelize from 'sequelize'

import userModel from './user'
import messageModel from './message'

const sequelize = new Sequelize(process.env.DATABASE, process.env.DATABASE_USER, process.env.DATABASE_PASSWORD, {
    dialect: 'postgres'
})

const models = {
    User: userModel(sequelize, Sequelize.DataTypes),
    Message: messageModel(sequelize, Sequelize.DataTypes)
}

Solution 3

I had the same issue and resolved it with:

Version 5.25.1
const model = sequelize["import"](path.join(__dirname, file));

Version 6.2.1
const model = require(path.join(__dirname, file))(sequelize, Sequelize);

Hope this helps

Solution 4

I was trying to find a way to do this using the class definition way of doing it. Here is how I did it!

models/trading-view-alert.js

const { Model, DataTypes } = require('sequelize');
module.exports = function(sequelize){
    class TradingViewAlert extends Model {}
    return TradingViewAlert.init({
      action: {
          type: DataTypes.STRING,
          allowNull: false
      },
      strategy_num_contracts: {
          type: DataTypes.STRING,
          allowNull: false
      },
      strategy_orderid: {
          type: DataTypes.STRING,
          allowNull: false
      },
      strategy_price: {
          type: DataTypes.STRING,
              allowNull: false
          },
      strategy_comment : {
          type: DataTypes.STRING,
          allowNull: false 
      },
      strategy_position_size: {
          type: DataTypes.STRING,
          allowNull: false  
      }
    }, {
      sequelize,
      modelName: 'TradingViewAlert'
    });
};

yourfileusingthemodel.js

const TradingViewAlert = require('../models/trading-view-alert');
const TradingViewAlertModel = TradingViewAlert(sequelize);

Then simply call await TradingViewAlertModel.sync({ alter: true });

The sync command of course needs to be within the async() function to work and there you go, classes with sequelize 6 with models in separate files!

Solution 5

If you have a lot of models, it might be useful to write an import method:

const autoImport = function(path) {
  let defineCall = require(path);
  if (typeof defineCall === 'object' && defineCall.__esModule) {
    // Babel/ES6 module compatability
    defineCall = defineCall['default'];
  }
  return defineCall(sequelize, Sequelize.DataTypes);
};

So instead of this:

...

const models = {
    Language: connection.import("./language"),
}

...

You could do that:

...

const models = {
    Language: autoImport("./language"),
}
...

Having 50+ models, it was easier for us to migrate to Sequelize 6.

Note: Use and implement solution 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply