Skip to content

models/index.js doesn't work with ES6 modules #960

Open

Description

@ngustavo

What you are doing?

Trying to load models with models/index.js doesn't work with ES6 modules activated with "type": "module"

const basename = path.basename(__filename);
// {...}
const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);

Variables like __filename and __dirname don't work anymore with modules.

What do you expect to happen?

There should be an option to choose between Modules or CommonJS.

What is actually happening?

There's only CommonJS support.

Activity

  1. Kulwch commented on Aug 15, 2021

    @Kulwch

    Hi,
    Having the same problem. Tried a long list of things to fix the problem, can't solve it. I'm stuck in a project i'm supposed to deliver in 2 weeks... Is there any chance someone has an idea of what to do? I searched for hours on the web, didn't find a useful thing.

  2. ngustavo commented on Aug 15, 2021

    @ngustavo
    Author

    For now, I created this temporary solution.

  3. Kulwch commented on Aug 16, 2021

    @Kulwch

    As a matter of fact, i occured this error since i didn't delete a model i created manually before using sequelize-cli... Thanks anyway for your answer, i'll keep it in mind !

  4. NixonAzeredo commented on Jan 5, 2022

    @NixonAzeredo

    This is my solution for the moment

    import { readdirSync } from "fs";
    import { basename, dirname } from "path";
    import { Sequelize, DataTypes } from "sequelize";
    import { fileURLToPath } from 'url';
    import database from "../config/database.js";
    
    const __filename = fileURLToPath(import.meta.url);
    const __dirname = dirname(__filename);
    
    const db = {};
    const sequelize = new Sequelize(database.development);
    
    export default (async () => {
      const files = readdirSync(__dirname)
        .filter(
          (file) => file.indexOf('.') !== 0
          && file !== basename(__filename)
          && file.slice(-3) === '.js',
        );
    
      for await (const file of files) {
        const model = await import(`./${file}`);
        const namedModel = model.default(sequelize, DataTypes);
        db[namedModel.name] = namedModel;
      }
    
      Object.keys(db).forEach((modelName) => {
        if (db[modelName].associate) {
          db[modelName].associate(db);
        }
      });
    
      db.sequelize = sequelize;
      db.Sequelize = Sequelize;
    	
      return db;
    })();
  5. WikiRik commented on Jan 13, 2022

    @WikiRik
    Member

    @ephys can you look into this?

  6. ephys commented on Jan 13, 2022

    @ephys
    Member

    I'm not sure how to go about fixing this.

    If we generate CJS code, it's not going to work for users that use ESM.
    If we generate ESM, it's not going to work for users that use CJS.

    I suppose we could prompt during generation which module system to use

  7. ngustavo commented on Jan 20, 2022

    @ngustavo
    Author

    I guess it's possible to use *.mjs, since it's a standard, just to differentiate. Then, at generation time, only copy those with the corresponding extension.
    There's also the package.json option, "type": "module". This would imply having premade packages, as CRA does.
    The last option is doing weird string interpolation.

  8. ephys commented on Jan 21, 2022

    @ephys
    Member

    If we were to generate .mjs, users using commonjs would not be able to require those files
    But I think we could just maintain both a mjs & cjs version and prompt the user to choose one (with the default being guessed from package.json)

  9. ngustavo commented on Feb 8, 2022

    @ngustavo
    Author

    That's what I meant by "differentiate" and "corresponding extension". It's the easiest way to go.
    I'm not sure if Node can figure it out on its own though when given duplicate files.

  10. ephys commented on Feb 8, 2022

    @ephys
    Member

    Well import requires specifying the file extension and require is unable to load esm so it should, but it would be cleaner to emit only one of them by asking the user the choose
    It shouldn't be a difficult to implement but I'm mostly focused on the main library right now - if someone wants to give a shot at a PR

    (The CLI code is also in dire need of an update and I'd like to migrate it to ESM before doing anything else too)

  11. mirzagirl commented on Dec 17, 2022

    @mirzagirl
  12. Yxn16a commented on Apr 21, 2023

    @Yxn16a

    This is my solution for the moment

    import { readdirSync } from "fs";
    import { basename, dirname } from "path";
    import { Sequelize, DataTypes } from "sequelize";
    import { fileURLToPath } from 'url';
    import database from "../config/database.js";
    
    const __filename = fileURLToPath(import.meta.url);
    const __dirname = dirname(__filename);
    
    const db = {};
    const sequelize = new Sequelize(database.development);
    
    export default (async () => {
      const files = readdirSync(__dirname)
        .filter(
          (file) => file.indexOf('.') !== 0
          && file !== basename(__filename)
          && file.slice(-3) === '.js',
        );
    
      for await (const file of files) {
        const model = await import(`./${file}`);
        const namedModel = model.default(sequelize, DataTypes);
        db[namedModel.name] = namedModel;
      }
    
      Object.keys(db).forEach((modelName) => {
        if (db[modelName].associate) {
          db[modelName].associate(db);
        }
      });
    
      db.sequelize = sequelize;
      db.Sequelize = Sequelize;
    	
      return db;
    })();

    This works, you just have to check if the file were imported before you call "model.default(sequelize,DataTypes) because you are awaiting for the files to be read other than that you are good to go" Also if the "const model = await import('./${file}') does not work just use const model = await import(path.resolve("src", "models", ${file})); after you have imported path from path then you should be good.

  13. inbal-samucha commented on May 21, 2023

    @inbal-samucha

    I write like this but it is'nt load the models and then give my the error:
    TypeError: model.default is not a function

    how can i fix it?

  14. jleweli commented on Jun 26, 2023

    @jleweli

    I write like this but it is'nt load the models and then give my the error: TypeError: model.default is not a function

    how can i fix it?

    I had the same issue. In my case models/index.js was loaded (which is the model loader and not a model. So the error message was correct). I wrote an additional .filter line to get it out of my file list. If this does not help you have to make sure that you are only importing models.

    I have a different problem. Following the solution of @NixonAzeredo I am trying to load models in my controller files:
    const User = sequelize.models.user; I get undefined doing so. The sequelize object which is exported from my models/index.js does not yet contain the models and I am wondering how I can fix this. I think this has to do with the async model loading. I tried out various things, but without any success, yet.

  15. Yxn16a commented on Jun 26, 2023

    @Yxn16a

    This works, what changes was to include path to the models.
    but remember to define your sequelize database connection somewhere. I am saying this in case you copy this code block. Other than that this should work. Hopefully this helps

    import { readdirSync } from "fs";
    // import path  here 
    import path from "path";
    import { basename, dirname } from "path";
    import { Sequelize, DataTypes } from "sequelize";
    import { fileURLToPath } from "url";
    import sequelize from "../config/connection.js";
    
    const __filename = fileURLToPath(import.meta.url);
    const __dirname = dirname(__filename);
    const db = {};
    export default (async () => {
      const files = readdirSync(__dirname).filter(
        (file) =>
          file.indexOf(".") !== 0 &&
          file !== basename(__filename) &&
          file.slice(-3) === ".js"
      );
    
      for await (const file of files) {
    // use path here to access your models from models directory then await for it @
        const model = await import(path.resolve("models", `${file}`));
        if (model.default) {
          const namedModel = await model.default(sequelize, DataTypes);
          db[namedModel.name] = namedModel;
        }
      }
    
      Object.keys(db).forEach((modelName) => {
        if (modelName) {
          if (db[modelName].associate) {
            db[modelName].associate(db);
          }
        }
      });
    
      db.sequelize = sequelize;
      db.Sequelize = Sequelize;
      return db;
    })();
    
  16. Yxn16a commented on Jun 26, 2023

    @Yxn16a
  17. jleweli commented on Jun 26, 2023

    @jleweli

    Thanks a lot for your effort. I tried it out and can confirm that it works. Unfortunately I ran into another issue: migrations seem not to work with ES6 modules out of the box. Possible solutions seem quite hacky. I decided to keep using CommonJS syntax and wait until there is better support for ES6.

  18. mahnunchik commented on Oct 20, 2023

    @mahnunchik
  19. heyimhere commented on Feb 3, 2024

    @heyimhere

    This works on my Nextjs 14.1.0, Node 19 and Sequelize 6.35

    'use strict';
    
    const fs = require('fs');
    const path = require('path');
    const Sequelize = require('sequelize');
    const process = require('process');
    const env = process.env.NODE_ENV || 'development';
    const config = require(__dirname + '/../config/config.js')[env];
    const db = {};
    
    const models = process.cwd() + '/db/models/' || __dirname;
    const basename = path.basename(models + "index.js");
    
    let sequelize;
    if (config.use_env_variable) {
      sequelize = new Sequelize(process.env[config.use_env_variable], config);
    } else {
      sequelize = new Sequelize({database: config.database, username: config.username,
        password: config.password, config: config, dialect: "mysql", host: config.host,
        dialectModule: require("mysql2")});
    }
    
    fs
      .readdirSync(models)
      .filter(file => {
        return (
          file.indexOf('.') !== 0 &&
          file !== basename &&
          file.slice(-3) === '.js' &&
          file.indexOf('.test.js') === -1
        );
      })
      .forEach(file => {
        const model = require(`./${file}`)(sequelize, 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;
    
    module.exports = db;
  20. sarakabariti commented on Apr 8, 2024

    @sarakabariti

    I just started learning node.js/express/Sequelize, I ran into this issue and spent hours trying to figure it out. I'm not very experienced and not sure if my solution is the best.. But what worked for me was to add a package.json file that just has the line {"type": "commonjs"} in each folder generated by Sequelize CLI (config, migrations, models). So I ended up with 3 of those files, one in each folder.

    This fixed the running migrate issue and importing the db and models in the rest of my files that use ES6.
    If there's a better solution please let me know...

  21. ephys commented on Apr 9, 2024

    @ephys
    Member

    models/index.js is going away in Sequelize 7, as the new model initialization makes it irrelevant

    If you want to use this file in ESM, here is what you need to do:

    • replace dynamic require with dynamic import. Keep in mind that it's async but if you use ESM you have access to top level await
    • __dirname and __filename can be replaced with import.meta.dirname and import.meta.filename respectively (node 21), or with this snippet of code:
    import { fileURLToPath } from 'node:url';
    import path from 'node:path';
    
    const __filename = fileURLToPath(import.meta.url);
    const __dirname = path.dirname(fileURLToPath(import.meta.url));

    Basically #960 (comment), with a small tweak, is the solution you should use if you want an equivalent to the CJS file for ESM. This is what we would generate if we were to update the CLI to support generating it as ESM.

    import { readdirSync } from "fs";
    import { basename, dirname } from "path";
    import { Sequelize, DataTypes } from "sequelize";
    import { fileURLToPath } from 'url';
    import database from "../config/database.js";
    
    const __filename = fileURLToPath(import.meta.url);
    const __dirname = dirname(__filename);
    
    const db = {};
    const sequelize = new Sequelize(database.development);
    
    const files = readdirSync(__dirname)
      .filter(
        (file) => file.indexOf('.') !== 0
        && file !== basename(__filename)
        && file.slice(-3) === '.js',
      );
    
    await Promise.all(files.map(async file => {
      const model = await import(`./${file}`);
      if (!model.default) {
        return;
      }
    
      const namedModel = model.default(sequelize, DataTypes);
      db[namedModel.name] = namedModel;
    }))
    
    Object.keys(db).forEach((modelName) => {
      if (db[modelName].associate) {
        db[modelName].associate(db);
      }
    });
    
    db.sequelize = sequelize;
    db.Sequelize = Sequelize;
    
    return db;
  22. purevoidov commented on Jun 19, 2025

    @purevoidov

    Here is the working example in es6. I configured model name line.

    const files = readdirSync(__dirname)
    .filter(
    (file) => file.indexOf('.') !== 0
    && file !== basename(__filename)
    && file.slice(-3) === '.js',
    );

    await Promise.all(files.map(async file => {
    const model = await import(./${file});
    if (!model.default) {
    return;
    }

    const namedModel = model.default(sequelize, Sequelize);
    const ccc = namedModel.name.split('_')
    const cname = ccc.length > 1 ? ccc[0].charAt(0).toUpperCase() + ccc[0].slice(1) + ccc[1].charAt(0).toUpperCase() + ccc[1].slice(1) : ccc[0].charAt(0).toUpperCase() + ccc[0].slice(1)
    db[cname] = namedModel;
    }))

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

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

    export default db

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions