SkyPower SkyPower - 26 days ago 21
Node.js Question

module.filename is undefined (Sequelize, Node.js)

I reading this tutorial for Sequelize and Node.js. I get the following error on line 4:

TypeError: Path must be a string. Received undefined

const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const basename = path.basename(module.filename);
const env = process.env.NODE_ENV || 'development';
const config = require(`${__dirname}/../config/config.json`)[env];
const db = {};

let sequelize;
if (config.use_env_variable) {
sequelize = new Sequelize(process.env[config.use_env_variable]);
} else {
sequelize = new Sequelize(
config.database, config.username, config.password, config
);
}
fs
.readdirSync(__dirname)
.filter(file =>
(file.indexOf('.') !== 0) &&
(file !== basename) &&
(file.slice(-3) === '.js'))
.forEach(file => {
const model = sequelize.import(path.join(__dirname, file));
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;


It seems that the module.filename is undefined. I have no idea why that is (I am new to Node) and the only differences from the tutorial and my app are that I don't use the express api (but GraphQL) and my sequelize auto generated folders are located in ../postgres/ instead of ../

Edit:

If I run the tutorial repo and print
module.filename
I get
/Users/Creece/Downloads/postgres-express-node-tutorial-master/server/models/index.js


Now if I manually assign the module.filename, like this:
module.filename = '/Users/Creece/Downloads/express_graphql_hmr_article_boilterplate-master/server/postgres/models/index.js';
(after the before the assignment of the basename constant) , I get a new weird error on the
const model = ...
line:

Error: Cannot find module '/Users/Creece/Downloads/express_graphql_hmr_article_boilerplate-master/server/dist/postgres/models/todo.js'


Note that the folder "dist" on the above path is webpack's (not sure about that though, it's just not mine).

Edit 2: (Regarding my comment on @NikMartin's answer

This is the autogenerated file by Sequelize ./postgres/models/todo.js:

module.exports = (sequelize, DataTypes) => {

const Todo = sequelize.define('Todo', {
title: {
type: DataTypes.STRING,
allowNull: false,
},
});



Todo.associate = (models) => {
Todo.hasMany(models.TodoItem, {
foreignKey: 'todoId',
as: 'todoItems',
});
};
return Todo;

};


Solution (based on the accepted answer):

const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
//const basename = path.basename(__filename);
const env = process.env.NODE_ENV || 'development';
const config = require(`${__dirname}/../config/config.json`)[env];
const db = {};

let sequelize;
if (config.use_env_variable) {
sequelize = new Sequelize(process.env[config.use_env_variable]);
} else {
sequelize = new Sequelize(
config.database, config.username, config.password, config
);
}

const models = [
require('./Todo')(sequelize, Sequelize),
require('./TodoItem')(sequelize, Sequelize),
];

models.forEach(model => {
db[model.name] = model;
});

models.forEach(model => {

if (db[model.name].associate) {
console.log("entered");
db[model.name].associate(db);
}
});

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

module.exports = db;

Answer Source

Regarding the module.filename problem you can read this discussion or just that comment: https://github.com/webpack/webpack/issues/1599#issuecomment-328540024

quote:

aprilmintacpineda commented

for those having this issue, it is also good to note that module.filename would be undefined if you bundled your files. In my case it was undefined and I fixed it by simply replacing it with __filename which yields the same outcome.

So you should be able to replace path.basename(module.filename); with path.basename(__filename);.

But it still would be a bundled js file in /dist folder and your models are in /postgres/models folder, as I understand.

I see here two options:

1) To not to automatically load your models, but directly require all necessary models.

assuming this file's path is /postgres/models/index.js and actual models are in the same folder not tested

const Sequelize = require('sequelize');
const env = process.env.NODE_ENV || 'development';
const config = require(`${__dirname}/../config/config.json`)[env];
const db = {};

let sequelize;
if (config.use_env_variable) {
  sequelize = new Sequelize(process.env[config.use_env_variable]);
} else {
  sequelize = new Sequelize(
    config.database, config.username, config.password, config
  );
}

// removed autoloading block

// setting the list of used models
const models = [
  require('./TodoItem')(sequelize, Sequelize),
  require('./Comment')(sequelize, Sequelize)
];

// registering models by their names
models.forEach(model => {
  db[model.name] = model;
});

// using registered models to set up relations
models.forEach(model => {
  if (model.associate) {
    model.associate(db);
  }
});

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

module.exports = db;

This would mean less flexibility, but your bundled code would start to use bundled models. I guess it's not the best thing for a dist code to require some "external" (missing internal models) non config js files to work.

2) To load files not from a current folder but from a defined one.

  • move /postgres/models/index.js out from the models folder to /postgres/index.js (so model folder would have only actual model files)
  • remove basename from the file
  • instead of .readdirSync(__dirname) use .readdirSync(__dirname + "/../postgres/models")
  • remove (file !== basename) && check (since it would be unnecessary)

Either way, replacing module's properties (module.filename) should not be a solution.