Unable to develop assosciations between models in sequelize - node.js

I am creating realtionship (one to many) between User model and Task model
I have crated the reference column in the migration too
I am using sequlize ORM with sequelize cli. Everything works fine when I try alternative method that i will explain later but If i try to do it according the documentation it gives me error
Task is not associated to User
WORKING ALTERNATIVE METHOD
IN end of index.js file I created relations and it works as expected
db.user = require("../models/user.js")(sequelize, Sequelize);
db.tasks = require("../models/Task.js")(sequelize, Sequelize);
db.user.hasMany(db.tasks, { as: "user_tasks",foreignKey: "userId" });
db.tasks.belongsTo(db.user, {
foreignKey: "userId",
as: "task_user",
});
but when i follow the documetation i got error that i mentioned earlier
According to document
Model User.js
module.exports = (sequelize, DataTypes) => {
const User =sequelize.define('User',{
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true,
},
name: {
type: DataTypes.STRING,
allowNull: false,
},
age: {
type: DataTypes.INTEGER,
},
email: {
type: DataTypes.STRING,
},
createdAt: {
allowNull: false,
type: DataTypes.DATE
},
updatedAt: {
allowNull: false,
type: DataTypes.DATE
}
});
User.associate = (models) =>{
User.hasMany(models.Tasks, {
as: 'user_tasks',
foreignKey: 'userId'}
);
};
return User;
};
Model: Task.js
module.exports = (sequelize, DataTypes) => {
const Task = sequelize.define('Task', {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true,
},
title: {
type: DataTypes.STRING,
allowNull: false,
},
userId: {
type: DataTypes.INTEGER,
},
});
Task.associate = function(models) {
Task.belongsTo(models.User, {
as:'owner',
foreignKey:'userId',
onDelete: "cascade"
});
};
return Task;
}
This is how i am trying to get data from database
controller
const User = db.user
const {Tasks} = db.tasks
exports.usertasks = async(req,res)=>{
// const users = await User(db.sequelize,db.Sequelize).findAll({
const users = await User.findAll({
include:[{model:Tasks,as:"user_tasks"}]
});
res.json(users)
console.log("All users with their associated tasks:", JSON.stringify(users, null, 4));
}
My 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 = require(path.join(__dirname+'../models/', file))(sequelize, Sequelize.DataTypes);
console.log(model);
db[model.name] = model;
});
Object.keys(db).forEach(modelName => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
db.sequelize = sequelize;
db.Sequelize = Sequelize;
//relations THIS IS WORKING AND I AM JUST CURIOUS WHETHER TO USE THIS APPROACH OR NOT
// db.user = require("../models/user.js")(sequelize, Sequelize);
// db.tasks = require("../models/Task.js")(sequelize, Sequelize);
// db.user.hasMany(db.tasks, { as: "user_tasks",foreignKey: "userId" });
// db.tasks.belongsTo(db.user, {
// foreignKey: "userId",
// as: "task_user",
// });
module.exports = db;
I am also attaching the picture of successful response that I get with the alternative method.
Kindly, guide whether the alternative method that I used is acceptable or should I stick to the documenation one?

.associate is not a native Model's method, so you need to call it manually.
I think in your index.js you want to do something like:
'use strict';
const Sequelize = require('sequelize');
const config = require('../config/config.json')[process.env.NODE_ENV || 'development'];
const requireAll = require('require-all');
const sequelize = config.use_env_variable
? new Sequelize(process.env[config.use_env_variable], config)
: new Sequelize(config.database, config.username, config.password, config);
const db = { sequelize };
const modelsInitializers = Object.values(requireAll({
dirname: __dirname,
recursive: false,
filter: (name) => name !== 'index.js' && name,
}));
const models = modelsInitializers.map((initializer) => {
const model = initializer(sequelize, Sequelize.DataTypes);
db[model.name] = model;
return model;
});
models.forEach((model) => {
if (model.associate) model.associate(db);
})
models.forEach((model) => {
if (model.associate) model.associate(models);
db[model.name] = model;
})
Object.entries().forEach(([modelName, model]) => {
model.associate(models);
db[modelName] = model;
});
module.exports = db;

Related

Error("No Sequelize instance passed") No Sequelize instance passed

Hello can somebody help me with this ? I was doing my controllers and I can access to my model like this "const { User } = require("../models/User");" but than when I send my request I had this message "TypeError: Cannot read property 'create' of undefined" so something was missing.
So I change my call model to this "const { User } = require("../models");".
And I went on my model index.js (so connection sequelize) and I add fs function and Objectif.key. After all those changes I have the error "No Sequelize instance passed".
So maybe somebody can help with this because I don't see the problem
So this is my model index.js
//sequelize importe
const fs = require("fs");
const path = require("path");
const Sequelize = require("sequelize");
const db = {};
const basename = path.basename(__filename);
let sequelize = new Sequelize("groupomania", "root", "root", {
host: "localhost",
dialect: "mysql",
});
sequelize
.authenticate()
.then(() => {
console.log("Connection has been established successfully!");
})
.catch((err) => {
console.log("Can't establish database connection:\n" + err);
});
fs.readdirSync(__dirname)
.filter((file) => {
console.log( file.indexOf(".") !== 0 && file !== basename && file.slice(-3) === ".js");
return (
file.indexOf(".") !== 0 && file !== basename && file.slice(-3) === ".js"
);
})
.forEach(file => {
const model = require(path.join(__dirname, 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;
This is my model User.js
const { Model } = require("sequelize");
module.exports = (Sequelize, DataTypes) => {
class User extends Model {
toJSON() {
return {
...this.get(),
password: undefined,
};
}
}
User.init(
{
id: { type: DataTypes.UUID, defaultValue: DataTypes.UUIDV4 },
nom: { type: DataTypes.STRING, allowNull: false },
prenom: { type: DataTypes.STRING, allowNull: false },
email: {
type: DataTypes.STRING,
allowNull: false,
validate: { notNull: true, notEmpty: true, isEmail: true },
},
status: { type: DataTypes.STRING, allowNull: false },
password: { type: DataTypes.STRING, required: true },
},
{ Sequelize, tableName: "users", modelName: "User" }
);
return User;
};
My controllers/user.js
/*const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");*/
const { User } = require("../models/User");
module.exports.signup = async (req, res) => {
try {
const user = await User.create({
nom: "Jp",
prenom: "remy",
email: "remy#gmail.fr",
password: "motdepasse",
});
res.status(200);
throw Error("erreur est survenu");
} catch (erreur) {
console.log(erreur);
res.status(200).json({ erreur });
}
};
My route user
const express = require("express");
const router = express.Router();
const userCtrl = require("../controllers/user");
router.post("/signup", userCtrl.signup);
/*router.post("/login", userCtrl.login);*/
module.exports = router;
Thank you for any help! :)
The answer was that on my model user the "S" of sequelize must have an s minus

Why sequelize is not creating all columns in the table for postgresql

I am creating a database module for my blog. I am using nodejs and sequelize module for this. The database I am using is Postgresql.
This is the sequelize initialization.
'use strict'
const Sequelize = require('sequelize')
// Creating a singletone
let sequelize = null
module.exports = function setupDatabase (config) {
if (!sequelize) {
sequelize = new Sequelize(config)
}
return sequelize
}
The singleton is to evaluate if a previus instance of sequelize was already created.
And then I have an script configuration:
'use strict'
const debug = require('debug')('diesanromero:db:setup')
const inquirer = require('inquirer')
const chalk = require('chalk')
const db = require('./')
const prompt = inquirer.createPromptModule()
async function setup () {
const answer = await prompt([
{
type: 'confirm',
name: 'setup',
message: 'This will destroy your database, are you sure?'
}
])
if (!answer.setup) {
return console.log('Nothing happend :)')
}
const config = {
database: process.env.DB_NAME || 'blog',
username: process.env.DB_USER || 'user',
password: process.env.DB_PASS || 'password',
host: process.env.DB_HOST || 'localhost',
dialect: 'postgres',
logging: s => debug(s),
setup: true
}
await db(config).catch(handleFatalError)
console.log('Success!')
process.exit(0)
}
function handleFatalError (err) {
console.error(`${chalk.red('[Fatal Error]')} ${err.message}`)
console.error(err.stack)
process.exit(1)
}
setup()
That is the script that creates the database, using the previous configuration.
Apparently all of this works fine. The database connects successfully. Then I have three models.
article.js
'use strict'
const Sequelize = require('sequelize')
const setupDatabase = require('../lib/db')
module.exports = function setupArticleModel (config) {
const sequelize = setupDatabase(config)
const stringRequired = {
type: Sequelize.STRING,
allowNull: false
}
const numberDefault = {
type: Sequelize.INTEGER,
allowNull: false,
default: 0
}
return sequelize.define('article', {
title: stringRequired,
slug: stringRequired,
description: stringRequired,
content: stringRequired,
likes: numberDefault,
shares: numberDefault
})
}
user.js
'use strict'
const Sequelize = require('sequelize')
const setupDatabase = require('../lib/db')
module.exports = function setupAuthModel (config) {
const sequelize = setupDatabase(config)
const stringRequired = {
type: Sequelize.STRING,
allowNull: false
}
return sequelize.define('user', {
name: stringRequired,
username: stringRequired,
email: stringRequired
})
}
auth.js
'use strict'
const Sequelize = require('sequelize')
const setupDatabase = require('../lib/db')
module.exports = function setupUserModel (config) {
const sequelize = setupDatabase(config)
const stringRequired = {
type: Sequelize.STRING,
allowNull: false
}
return sequelize.define('auth', {
salt: stringRequired,
hash: stringRequired,
email: {
type: Sequelize.INTEGER,
allowNull: false
}
})
}
And then my main file index.js
'use strict'
const defaults = require('defaults')
const setupDatabase = require('./lib/db')
const setupUserModel = require('./models/user')
const setupArticleModel = require('./models/article')
module.exports = async function (config) {
config = defaults(config, {
dialect: 'sqlite',
pool: {
max: 10,
min: 0,
idle: 10000
},
query: {
raw: true
}
})
const sequelize = setupDatabase(config)
const UserModel = setupUserModel(config)
const AuthModel = setupAuthModel(config)
const ArticleModel = setupArticleModel(config)
UserModel.hasMany(ArticleModel)
UserModel.hasOne(AuthModel)
ArticleModel.belongsTo(UserModel)
AuthModel.belongsTo(UserModel)
await sequelize.authenticate()
if (config.setup) {
await sequelize.sync({ force: true })
}
const User = {}
const Auth = {}
const Article = {}
return {
User,
Auth,
Article
}
}
This main file connects with sequelize.authentication successfully, and then performs migrations. Note that the User model has a 1 to 1 relationship with the Authentication model. Then the User model has a 1 to many relationship with the Article model.
It happens that the ORM does not create all the columns in the respective tables. Some if created, others not.

Query in Sequelize getProducts method

I am implementing an E-Commerce site in NodeJS. I am using Sequelize for managing MySQL.
But I am getting an error while using getProducts() method.
The code of the function where I am using the getProducts() mehtod is as below
exports.postCart = (req, res, next) => {
const p_id = req.body.p_id;
// console.log(p_id);
let fetchedCart;
let newQuantity = 1;
req.user
.getCart()
.then((cart) => {
fetchedCart = cart;
console.log(p_id);
return cart.getProducts({ where: { id: p_id } });
})
.then((products) => {
let product;
if (products.length > 0) {
product = products[0];
}
if (product) {
// ....
}
return Product.findByPk(p_id)
})
.then((product) => {
return fetchedCart.addProduct(product, {
through: { quantity: newQuantity }
});
})
.then(() => {
res.redirect("/cart");
})
.catch((err) => {
console.log(err);
});
};
I am following a tutorial which I think is using an older version of sequelize. I am using 6.3.0 version of sequelize. So is the method getProducts() not available in sequelize 6 or an I am typing something wrong.
Any help is welcomed :D
Thanks in advance :)
Associations file
const http = require("http");
const path = require("path");
const express = require("express");
const bodyParser = require("body-parser");
const pageNotFoundController = require("./controllers/pageNotFound");
const sequelize = require("./util/database");
const Product = require('./models/product');
const User = require('./models/user');
const Cart = require('./models/cart');
const CartItem = require('./models/cart-item');
const app = express();
app.set("view engine", "ejs");
app.set("views", "views");
const adminRoutes = require("./routes/admin");
const shopRoutes = require("./routes/shop");
app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, "public")));
app.use((req, res, next) => {
User.findByPk(1)
.then(user => {
req.user = user;
next();
})
.catch(err => {
console.log(err);
});
});
app.use("/admin", adminRoutes);
app.use(shopRoutes);
app.use(pageNotFoundController.getPageNotFound);
Product.belongsTo(User, {constraints: true, onDelete: 'CASCADE'});
User.hasMany(Product);
User.hasOne(Cart);
Cart.belongsTo(User);
Cart.belongsToMany(Product, { through: CartItem });
Product.belongsToMany(Cart, { through: CartItem });
sequelize
// .sync({ force: true })
.sync()
.then((result) => {
return User.findByPk(1);
// console.log(result);
})
.then(user => {
if (!user) {
User.create({name: "Prerak", email: "pmp.tech2000#gmail.com"});
}
return user;
})
.then(user => {
console.log(user);
})
.then(cart => {
app.listen(3000);
})
.catch((err) => {
console.log(err);
});
Models file
model - cart.js
const Sequelize = require('sequelize')
const sequelize = require('../util/database')
const Cart = sequelize.define('cart', {
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true
}
});
module.exports = Cart;
model - cart-item.js
const Sequelize = require("sequelize");
const sequelize = require("../util/database");
const CartItem = sequelize.define("cartItem", {
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true,
},
quantity: {
type: Sequelize.INTEGER,
},
});
module.exports = CartItem;
model - product.js
const Sequelize = require("sequelize");
const sequelize = require("../util/database");
const Product = sequelize.define("product", {
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true,
},
title: Sequelize.STRING,
price: {
type: Sequelize.DOUBLE,
allowNull: false,
},
imageUrl: {
type: Sequelize.STRING,
allowNull: false,
},
description: {
type: Sequelize.STRING,
allowNull: false,
},
});
module.exports = Product;
model - user.js
const Sequelize = require("sequelize");
const sequelize = require("../util/database");
const User = sequelize.define("user", {
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true,
},
name: {
type: Sequelize.STRING,
allowNull: false,
},
email: {
type: Sequelize.STRING,
allowNull: true,
},
});
module.exports = User;

Sequelize ORM can't use sync()

This is an extract from a file that uses db as the variable from the db.js file, which is the file which connects to the database. And this works fine.
const Sequelize = require("sequelize")
const db = require("../database/db.js")
module.exports = db.sequelize.define(
"physics_tbls", {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},...
When I add db.sync() to the end of this file, I get an error. This is the end of file above,
.......
Sub_part: {
type: Sequelize.STRING
}
}, {
timestamps: false
}
)
db.sync({
// force: true
})
.then(() => {
console.log('Connection to database established successfully.');
})
.catch(err => {
console.log('Unable to connect to the database: ', err);
})
I get the error,
TypeError: db.sync is not a function
If db.sequelize works why does db.sync() not work?
Perhaps db.sync should be in the same file as db.js which contains the variable db.But I do need to define my models before I can sync().
This is db.js
const Sequelize = require("sequelize")
const db = {}
const sequelize = new Sequelize("physics_papers_db", "root", "********", {
host: "localhost",
dialect: "mysql",
// operatorsAliases: false,
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000
}
})
db.sequelize = sequelize
db.Sequelize = Sequelize
module.exports = db
My Express routes incidentally are in another file called tasks.js
const Sequelize = require('sequelize')
var express = require("express")
var router = express.Router()
const Task = require("../model/Task")
//Test
router.get("/test", (req, res) => {
Task.findAll({
//Use attributes to only select specific fields
attributes: ['Question', 'Marks']
})
.then(tasks => {
console.log("router.get(\"/test\", (req, res) => {...");
console.log("All tasks:", JSON.stringify(tasks, null, 4));
res.json(tasks)
})
.catch(err => {
res.send("error: " + err)
})
})
........... more routes...
module.exports = router
Thanks,
#l2ysho answer is right, but I would add a bit of work so you can avoid the "mess" you are doing in your code... when using an ORM, the general idea is to help us, not only connect to the database, or not writing any SQL, or protect from SQL injection, but mostly be easy to use and change...
with that in mind, always do this when using Sequelize:
create a models folder in your project
add an index.js inside that folder with:
const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const _ = require('lodash');
const db = {};
const sequelize = new Sequelize(process.env.DB_DATABASE, process.env.DB_USER, process.env.DB_PWD, {
host: process.env.DB_HOST,
dialect: 'mysql',
logging: process.env.APP_ENVIRONMENT !== 'local' ? null : console.log,
pool: {
max: 5,
min: 0,
idle: 10000,
},
operatorsAliases: false,
});
// add all models
fs.readdirSync(__dirname)
.filter(file => file.indexOf('.') !== 0 && file !== 'index.js' && file.slice(-3) === '.js')
.forEach(file => {
const model = sequelize.import(path.join(__dirname, file));
db[model.name] = model;
});
// add all associations
Object.keys(db).forEach(modelName => {
if ('associate' in db[modelName]) {
db[modelName].associate(db);
}
});
module.exports = _.extend(
{
sequelize,
Sequelize,
},
db
);
now, add a file per database model, let's imagine a logs table you might have:
called it logs.js and put inside the models folder, together with the index.js
module.exports = (sequelize, DataTypes) => {
const model = sequelize.define(
'external_logs',
{
id: {
type: DataTypes.INTEGER.UNSIGNED,
primaryKey: true,
autoIncrement: true,
allowNull: false,
},
engine: { type: DataTypes.STRING, allowNull: false },
response_code: { type: DataTypes.INTEGER.UNSIGNED, allowNull: true },
origin: { type: DataTypes.STRING, allowNull: true },
created_at: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: sequelize.literal('CURRENT_TIMESTAMP'),
},
},
{
timestamps: false,
tableName: 'frontend_api_external_logs',
}
);
model.associate = models => {
model.belongsTo(models.campaigns, {
foreignKey: 'campaign_id',
allowNull: false,
onDelete: 'cascade',
onUpdate: 'cascade',
});
model.belongsTo(models.external_audit, {
foreignKey: 'audit_id',
allowNull: false,
onDelete: 'cascade',
onUpdate: 'cascade',
});
};
return model;
};
as you can see, you can easily create associations with other models very easy, without the need to import any file, and you can then call this model, by the definition you specified, in this case: external_logs ... though the real table name is called, as you can see: frontend_api_external_logs
if you end up needed to call any model or perform any database task, all you need is to call that index.js file, and not only the database, but also all models with their own associations will be available as well... for example, in a utilities folder, with a database.js file, the database.js could simply have:
const db = require('./../models'); // same as './../models/index'
const logApiCall = (action, description) => {
const logRow = {
// content of the external logs table
external_audit: {
// content of the audit table as there's an association
}
}; // the content
db.external_logs
.create(logRow, { include: db.external_audit })
// inserted
})
.catch(err => {
// NOT inserted
logger.log(`AUDIT ERROR: ${err.message}`);
utilities.slack.sendMessage({ action, description, error: err.message });
});
};
exports.logCall = logApiCall;
and this makes it even easier to perform logs with audits to any action performed by the users, and it will as simple as:
const db = require('./../utilities/database');
...
db.logCall('my action', 'my description');
as you can see, I don't need to worry about what models my file is (no need to require each model I'm using) and I simply call one file that handles all.
this is what I've been using since I've started to use Sequielize (when there was only v2 available) and I've been "upgrading" the index.js file when they bump versions, but this one is the exact same for ev4 and the new v5...
give it a try and I'm sure it will help you along your Sequielize projects...
in a new project of mine:
It is correct behaviour, you export sequelize instance and Sequelize class, so you should use db.sequelize.sync()

TypeError: Cannot read property 'define' of undefined

i try to change my Db from mongodb to mysql, i use sequelize instead of mongoose, and i got this error, i created the User.js model with sequelize format, but something wrong and i don't know why
const User = sequelize.define('users', {
^
TypeError: Cannot read property 'define' of undefined"
Here is my code:
server.js
const Sequelize = require('sequelize');
// DB Config
const db = require('./config/keys');
// Connect to MySql
const sequelize = new Sequelize(db.database, db.user, db.password, {
host: db.host,
dialect: 'mysql',
port: db.port
});
// Test the connection
sequelize
.authenticate()
.then(() => {
console.log('Connection has been established successfully.');
})
.catch(err => {
console.error('Unable to connect to the database:', err);
});
const serverDB = {};
serverDB.sequelize = sequelize;
serverDB.Sequelize = Sequelize;
module.exports = serverDB;
Users.js
const serverDB = require('../server');
const sequelize = serverDB.sequelize;
const Sequelize = serverDB.Sequelize;
const User = sequelize.define('users', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: {
type: Sequelize.STRING,
allowNull: false
},
email: {
type: Sequelize.STRING,
allowNull: false
},
password: {
type: Sequelize.STRING,
allowNull: false
},
avatar: {
type: STRING
},
date: {
type: Sequelize.DATE,
defaudefaultValue: Sequelize.NOW
}
});
sequelize.models.modelName
// sequelize.sync()
// .then(() => {
// console.log('User db and user table have been created')
// });
module.exports = User;
You should add
const Sequelize = require('sequelize');
in your client too.
And only export and use the lowercase sequelize.
And ensure relative path of server.js is good.

Resources