sequelize / express-example

A proposal for the usage of Sequelize within an Express.JS application.
MIT License
2.5k stars 770 forks source link

Packaging with Electron #71

Closed aprilmintacpineda closed 7 years ago

aprilmintacpineda commented 7 years ago

I use the same approach. I use sequelize with Electron, the problem is, when I package my app, it throws an error saying that models directory is not defined. How do you handle that?

Theoretically, I am thinking that if I simply import them and attach them to db it should work, but I actually have to use sequelize['import']() for that do I? since I bundle all my files I should just import all my models then attach them to db.

import fs from 'fs';
import path from 'path';
import sequelize from '../main_process/Database';
import Sequelize from 'sequelize';

let basename = path.basename(__filename);
let db = {};

fs.readdirSync(__dirname)
.filter(function(file) {
  return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
})
.forEach(function(file) {
  let 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;
aprilmintacpineda commented 7 years ago

I have figured out a solution for this.

cashwanikumar commented 6 years ago

@aprilmintacpineda I am also facing same issue. Can you please share your findings?

lucawen commented 6 years ago

can't tell us how you solve this?

lysla commented 6 years ago

Hi, I'm having the same issue and I'm stuck with this for hours, I'd really appreciate if someone could share a solution. I'm working with latest versions of Electron, Sequelize, and ExpressJS

Right now I have my models/index.js file

'use strict';

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], config);
} else {
  sequelize = new Sequelize(config.database, config.username, config.password, config);
}

fs
  .readdirSync(__dirname)
  .filter(file => {
    return (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;

And my main server.js file

const express = require('express');  
const bodyParser = require('body-parser');  
// here the call to the sequelize models
const db = require('./database/models/index.js');

const app = express();

app.use(bodyParser.json()); 

app.get('/api/events', (req, res) => {
    return db.Event.findAll()
        .then((events) => res.send(events))
        .catch((err) => {
            console.log('There was an error querying events', JSON.stringify(err))
            return res.send(err)
        });
});

app.listen(3000, () => {  
    console.log('Server is up on port 3000');
});

Then i require the server.js right into the main electron js file (for me is background.js)

When i try to serve with electron, my server goes up and running, but everytime i try to http request the "/api/events/" get, I keep getting this:

TypeError: Cannot read property 'findAll' of undefined

Because it doesn't find my "Event" model, I am guessing cause it doesn't loop correctly throu the models js files since I don't see them anywhere in the dist builded folder. Note: when I try to run the server straight with

node src/server.js

Everything works fine... It's like Electron doesn't deal well with Sequelize models.

cashwani commented 6 years ago

problem is with this code fs .readdirSync(__dirname) .filter(file => { return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js'); }) .forEach(file => { const model = sequelize['import'](path.join(__dirname, file)); db[model.name] = model; }); When you create a build it can not find any file that is why Event is not found.

You have to hardcore require the model in case of electron above code is okay only if this runs in nodejs

cashwani commented 6 years ago

@lysla Below is just a logic, you can refactor / change, I did same time ago and its working that time

index.js

import Sequelize from 'sequelize';
import { sequelize, config } from '../config/DatabaseConnection';

let models = {};

const DB = (function () {
  if (Object.keys(models).length) {
    return models;
  }

  let modules = [
    require('./categoryModel'),
    require('./conditionModel'),
  ];

  // Initialize models
  modules.forEach((module) => {
    const model = module(sequelize, Sequelize, config);
    models[model.name] = model;
  });

  // Apply associations
  Object.keys(models).forEach((key) => {
    if ('associate' in models[key]) {
      models[key].associate(models);
    }
  });

  models.sequelize = sequelize;
  models.Sequelize = Sequelize;

  return models;
})();

module.exports = DB;

`

use like import DB from '../models'; DB.category.findById(id, {......}

lysla commented 6 years ago

@cashwani Oh my, I'm really thankful. That managed to make it work, thank you so much! ❤ For clarity, that's how my index.js files look now:

const Sequelize = require('sequelize');
const env = process.env.NODE_ENV || 'development';
const config = require(__dirname + '/../config/config.json')[env];

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

let models = {};

const db = (function () {
  if (Object.keys(models).length) {
    return models;
  }

  let modules = [
    require('./event')
  ];

  modules.forEach((module) => {
    const model = module(sequelize, Sequelize, config);
    models[model.name] = model;
  });

  Object.keys(models).forEach((key) => {
    if ('associate' in models[key]) {
      models[key].associate(models);
    }
  });

  models.sequelize = sequelize;
  models.Sequelize = Sequelize;

  return models;
})();

module.exports = db;

I don't want to run too much offtopic, but I was wondering if the way I am using expressjs inside electron is proper. I mean, it works, but I'm unsure its the best way. Resuming, I have a server.js where I init my expressjs server that function like a local api, interacting to a local sqlite3 database via sequelize. I just put a

require('./server')

inside of the main electron js file (the one that holds the 'createWindow'). Nothing else. Is that right? Feels casual 🤔

sam-rusty commented 6 years ago

Here is how i would do this, much cleaner approach.

    const {db} = require('../config');
    const Sequelize = require('sequelize');
    const fs = require('fs');
    const async = require('async');

    let sequelize = new Sequelize(db.database, db.user, db.password, {
       host: db.host || 'localhost',
        dialect: 'mysql',
        logging: false,
        pool: {
            max: 5,
            min: 0,
            acquire: 30000,
            idle: 10000
        },
        operatorsAliases: false
    });

    let db = {};
    // Read the models directory to load all models
    fs.readdir("./models", (errs, files) => {
        async.forEach(files, (item, cb) => {
            const ModelName = item.replace('.js', '');
            db[ModelName] = require('./models/' + item)(sequelize);
            cb();
        }, async () => {
            try {
                await sequelize.sync({alter: true});
            } catch (e) {
                console.log('error while sequelize sync');
            }
        });
    });
    module.exports = db;

My Model Look like this: user.js

    const Sequelize = require("sequelize");
    module.exports = (sequalize) => {
        return sequalize.define('user', {
            first_name: Sequelize.STRING,
            last_name: Sequelize.STRING,
            email: Sequelize.STRING,
            password: Sequelize.STRING,
            createdAt: {
               type: Sequelize.DATE,
                allowNull: true
            },
            updatedAt: {
                type: Sequelize.DATE,
                allowNull: true
            },
        });
    };
tobidsn commented 4 years ago

Here is how i would do this with sqlite database models/index.js

'use strict'

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

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

fs
  .readdirSync(__dirname)
  .filter(file => {
    return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
  })
  .forEach((file) => {
    var model = sequelize['import'](path.resolve(__dirname, file));
    database[model.name] = model
  })

for (var modelName in database) {
  if (database[modelName].associate) {
    database[modelName].associate(database);
  }
}

database.sequelize = sequelize
database.Sequelize = Sequelize

module.exports = database

My Model look like this : masterunit.js

'use strict';
module.exports = (sequelize, DataTypes) => {
  const MasterUnit = sequelize.define('MasterUnit', {
    id: {
        type:DataTypes.INTEGER,
        primaryKey: true
    },
    unit: DataTypes.STRING,
    level: DataTypes.STRING
  }, {});
  MasterUnit.associate = function(models) {
    // associations can be defined here
  };
  return MasterUnit;
}

and My config/config.json

{
  "development": {
    "dialect": "sqlite",
    "storage": "./.temp/database.db"
  },
  "test": {
    "dialect": "sqlite",
    "storage": ":memory"
  },
  "production": {
    "dialect": "sqlite",
    "storage": "./.temp/database.db"
  }
}