node.JS / sequelize associate db tables - node.js

I am trying to associate two database tables, but I do not know exactly where to add the code in my configuration:
config.js
const Sequelize = require("sequelize");
const sequelize = new Sequelize("training", "postgres", "test", {
host: "localhost",
dialect: "postgres",
dialectOptions: {
ssl: {
require: true,
rejectUnauthorized: false // <<<<<<< YOU NEED THIS
}
},
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000,
},
})
const db = {};
db.Sequelize = Sequelize;
db.sequelize = sequelize;
// db.sequelize.gallery = require("../models/gallery")(DataTypes);
// db.tags = require("../models/tags.js")(sequelize, Sequelize);
// gallery.hasMany(db.tags, { as: "item" });
// db.tags.belongsTo(db.gallery, {
// foreignKey: "item",
// as: "item",
// });
module.exports = { db, JWT_SECRET: "secretString",}
If I add the commented code here I get the error that require is not a function.
This is how my models look like:
gallery.js
const { db } = require("../config/configProvider")();
module.exports = function(DataTypes) {
const Gallery = db.sequelize.define(
"items",
{
// },
date: {
type: DataTypes.DATE(),
// required: true,
},
url: {
type: DataTypes.STRING,
// required: true,
},
description: {
type: DataTypes.STRING,
},
location: {
type: DataTypes.STRING,
},
rating: {
type:DataTypes.INTEGER,
}
},
{ timestamps: false }
);
return Gallery;
};
index.js
const sequelize = require("sequelize");
const DataTypes = sequelize.DataTypes;
const Types = require("./models/types")(sequelize, DataTypes);
const Tags = require("./models/tags")(sequelize, DataTypes);
config.db.sequelize
.authenticate()
.then(() => {
console.log("Connection has been established successfully.");
})
.catch((err) => {
console.error("Unable to connect to the database:", err);
});
If I create index.js in models (where I could add the db associations, how would I call it in main file index.js?

gallery.js
Gallery.associate = function(models) {
gallery.hasMany(db.tags, { as: "item" });
};
tag.js
Tag.associate = function (models) {
tags.belongsTo(db.gallery, {
foreignKey: "item",
as: "item",
});
}

Related

Sequelize Associations in Node/express JS

I am new to sequelize SQL ORM nodejs. I am facing problem with associations.When I try to make a join query. It throws an association error. here is my code snippet.
server.js
app.get("/", (req, res) => {
db_conn.syncModels(req, res);
});
db_connection.js
require("dotenv").config();
const { Sequelize, DataTypes } = require("sequelize");
const crypto = require("crypto");
class DbConnection {
constructor() {
this.db = {};
this.sequelizeObj = new Sequelize(
process.env.DB_NAME,
process.env.DB_USER,
process.env.DB_PASS,
{
host: process.env.DB_URI,
dialect: "mysql",
logging: true,
}
);
this.userRoles = [
{
role_name: "SuperAdmin",
},
{
role_name: "Admin Assistant",
},
{
role_name: "HR Manager",
},
{
role_name: "Department Head",
},
{
role_name: "Assistant Manager",
},
{
role_name: "Employee",
},
];
this.defaultUser = {
user_name: "SuperAdmin",
user_email: "superadmin#" + process.env.BASE_URL,
user_password: crypto
.createHash("md5")
.update("SuperAdmin")
.digest("hex"),
role_id: 100000,
};
}
sequelize() {
return this.sequelizeObj;
}
connect() {
this.sequelizeObj
.authenticate()
.then(() => {
console.log("Connection has been established successfully.");
})
.catch((error) => {
if (error.message.includes("Unknown database")) {
this.createdb();
} else {
console.error("Unable to connect to the database: ", error);
}
});
}
async createdb() {
try {
const sequelize = new Sequelize(
"",
process.env.DB_USER,
process.env.DB_PASS,
{
host: process.env.DB_URI,
dialect: "mysql",
logging: true,
}
);
const createdb = await sequelize.query(
`CREATE DATABASE IF NOT EXISTS ${process.env.DB_NAME};`
);
console.log("Database Created")
} catch (err) {
console.log(err);
}
}
disconnect() {
this.sequelizeObj
.close()
.then(() => {
console.log("Connection has been closed successfully.");
})
.catch((error) => {
console.error("Unable to disconnect: ", error);
});
}
syncModels(payload, cb) {
this.db.Sequelize = Sequelize;
this.db.sequelize = this.sequelizeObj;
this.db.sequelize
.sync({ force: true })
.then((res) => {
console.log("Environment Configured");
})
.then(async () => {
await this.db.user_roles.bulkCreate(this.userRoles);
})
.then(() => {
console.log("User Roles Created");
})
.then(() => {
cb.status(200).send("Environment Configured");
})
.catch((err) => {
cb.status(400).send("Environment Configuration Error " + err);
});
// specifying models
this.db.users = require("../Auth/models/auth.model")(
this.sequelizeObj,
DataTypes
);
this.db.user_roles = require("../Auth/models/roles.model")(
this.sequelizeObj,
DataTypes
);
// Method 01
// Relation of user and user-roles
this.db.users.hasOne(this.db.user_roles, {
foreignKey: "role_id",
as: "role",
});
this.db.user_roles.belongsTo(this.db.users, {
foreignKey: "role_id",
as: "role",
});
}
random_password(length) {
var result = "";
var characters =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
var charactersLength = characters.length;
for (var i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
}
module.exports = DbConnection;
Users.model.js
module.exports = (sequelize, DataTypes) => {
const Users = sequelize.define(
"users",
{
user_id: {
type: DataTypes.INTEGER,
allowNull: false,
autoIncrement: true,
primaryKey: true,
unique: true,
},
user_name: {
type: DataTypes.STRING,
allowNull: false,
},
user_email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
user_password: {
type: DataTypes.STRING,
allowNull: false,
},
role_id: {
type: DataTypes.INTEGER,
allowNull: false,
},
},
{
initialAutoIncrement: 1900000,
}
);
// Method 02
Users.associate = (models) => {
Users.hasOne(models.UserRoles, { foreignKey: "role_id" });
};
return Users;
};
UserRoles.model.js
module.exports = (sequelize, DataTypes) => {
const UserRoles = sequelize.define(
"user_roles",
{
role_id: {
type: DataTypes.INTEGER,
allowNull: false,
autoIncrement: true,
primaryKey: true,
unique: true,
},
role_name: {
type: DataTypes.STRING,
allowNull: false,
},
},
{
initialAutoIncrement: 100000,
}
);
// Method 02
UserRoles.associate = (models) => {
UserRoles.hasMany(models.Users, { as: "users", foreignKey: "user_id" });
};
return UserRoles;
};
My query is as follows:
findUser(body, callback) {
let jwt = new JsonWebToken();
let encrypted = crypto
.createHash("md5")
.update(body.password)
.digest("hex");
this.Users.findOne({
includes: [
model: 'user_roles'
]
where: {
user_email: body.email,
user_password: encrypted,
},
})
.then((res) => {
console.log(res)
})
.catch((err) => {
callback.status(302).send("Error Found" + err);
});
}
The error is throws as follows:
Error FoundSequelizeEagerLoadingError: user_roles is not associated to users!
Any help would be appreciated.
Thanks

Sequelize associations with multiple model files

(Update below)
I'm trying to set up model associations using Sequelize and the .associate method.
I have the setup below but this returns an error:
UnhandledPromiseRejectionWarning: SequelizeEagerLoadingError:
ProductMember is not associated to Product!
What am I doing wrong?
Db setup:
const { Sequelize } = require("sequelize");
const config = require("./db.config.js");
const sequelize = new Sequelize(config[process.env]);
module.exports = sequelize;
db.config.js:
module.exports = {
development: {
database: DB_NAME,
username: DB_USER,
password: DB_PASS,
dialect: DB_DRIVER,
logging: false,
options: {
host: DB_HOST,
port: DB_PORT,
pool: {},
},
},
}
/models/product.js:
const { DataTypes } = require("sequelize");
const sequelize = require("../db");
const Product = sequelize.define(
"Product",
{
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false,
},
name: {
type: DataTypes.STRING(255),
unique: true,
allowNull: false,
},
}
{
tableName: "products",
}
}
Product.associate = (models) => {
Product.belongsToMany(models.User, {
through: models.ProductMember,
foreignKey: "product_id",
otherKey: "user_id",
});
Product.hasMany(models.ProductMember, {
foreignKey: "product_id",
allowNull: false,
});
};
module.exports = sequelize.model("Product", Product);
models/user.js:
const { DataTypes } = require("sequelize");
const sequelize = require("../db");
const User = sequelize.define(
"User",
{
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false,
},
email: {
type: DataTypes.STRING(255),
allowNull: false,
unique: true,
isEmail: true,
},
username: {
type: DataTypes.STRING(15),
allowNull: false,
unique: true,
},
},
{
tableName: "users",
}
);
User.associate = (models) => {
User.belongsToMany(models.Product, {
through: models.ProductMember,
foreignKey: "user_id",
otherKey: "product_id",
});
User.hasMany(models.ProductMember, {
foreignKey: "user_id",
allowNull: false,
});
User.belongsToMany(models.Product, {
through: models.UserFavouriteProducts,
foreignKey: "user_id",
otherKey: "product_id",
});
}
module.exports = sequelize.model("User", User);
models/productMember.js:
const { DataTypes } = require("sequelize");
const sequelize = require("../db");
const ProductMember = sequelize.define(
"ProductMember",
{
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false,
},
isAdmin: {
type: DataTypes.BOOLEAN,
defaultValue: false,
},
},
{
tableName: "product_members",
}
);
ProductMember.associate = (models) => {
ProductMember.belongsTo(models.User);
ProductMember.belongsTo(models.Product);
};
module.exports = sequelize.model("ProductMember", ProductMember);
Update: Based on this post I updated the Db setup file to:
const fs = require('fs');
const path = require('path');
var basename = path.basename(module.filename);
const models = path.join(__dirname, '../models');
const db = {};
const Sequelize = require("sequelize");
const config = require("./db.config.js");
const sequelize = new Sequelize(config[process.env]);
fs
.readdirSync(models)
.filter(function (file) {
return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
})
.forEach(function (file) {
var model = require(path.join(models, file))(
sequelize,
Sequelize.DataTypes
);
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;
Model file:
module.exports = (sequelize, Sequelize) => {
const Coupon = db.sequelize.define(
//... continue as it was
return Coupon;
}
So for the model file:
Wrapped it inside module.exports = (sequelize, Sequelize) => { }
return Coupon at the end
removed const sequelize = require("../db");
New problem: But with this new setup, Sequelize-related controller methods no longer work... For example for a controller file:
const User = require("../models/user");
const ensureLoggedIn = async (req, res, next) => {
...
const user = await User.findByPk(id);
it produces the error:
User.findByPk is not a function
I've tried adding const sequelize = require("../db"); to the controller file and then const user = await sequelize.User.findByPk(id); but that produced the same error.
I've tried adding const db = require("../db"); to the controller file and then const user = await db.sequelize.User.findByPk(id); but that produced the error "Cannot read property 'findByPk' of undefined".
First, you don't need to call sequelize.model to get a model that registered in Sequelize, you can just export a return value from sequelize.define:
const Product = sequelize.define(
"Product",
...
module.exports = Product;
Second, it seems you didn't call associate methods of all registered models.
Look at my answer here to get an idea of how to do it.
Update: to use models if you defined them like in my answer you need to access them like this:
const { sequelize } = require("../db");
const user = await sequelize.models.User.findByPk(id);
...

NodeJS PostgreSQL Express SequelizeEagerLoadingError on relationship table

I'm having a trouble where I couldn't access the relationship data table from NodeJS PostgreSQL and Express.
First of all here is my simple table :
As you can see, my "jenis" table has association with "kategori" table, one jenis can have multiple kategori, and one kategori should only contain one jenis.
Here is my migration on Postgres Sequelize :
jenis-migration.js
"use strict";
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable("Umkm_Jenis", {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER,
},
nama: {
type: Sequelize.STRING,
},
createdAt: {
allowNull: false,
type: Sequelize.DATE,
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE,
},
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable("Umkm_Jenis");
},
};
kategori-migration.js
"use strict";
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable("Umkm_Kategori", {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER,
},
jenis_id: {
type: Sequelize.INTEGER,
allowNull: false,
},
nama: {
type: Sequelize.STRING,
allowNull: false,
},
createdAt: {
allowNull: false,
type: Sequelize.DATE,
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE,
},
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable("Umkm_Kategori");
},
};
jenis.model.js
"use strict";
const { Model } = require("sequelize");
module.exports = (sequelize, DataTypes) => {
class Umkm_Jenis extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
this.hasMany(models.Umkm_Kategori, {
foreignKey: "jenis_id",
as: "umkm_kategori",
});
}
}
Umkm_Jenis.init(
{
nama: DataTypes.STRING,
},
{
sequelize,
modelName: "Umkm_Jenis",
freezeTableName: true,
}
);
return Umkm_Jenis;
};
kategori-model.js
"use strict";
const { Model } = require("sequelize");
module.exports = (sequelize, DataTypes) => {
class Umkm_Kategori extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
this.belongsTo(models.Umkm_Jenis, {
foreignKey: "jenis_id",
as: "umkm_jenis",
});
}
}
Umkm_Kategori.init(
{
jenis_id: DataTypes.INTEGER,
nama: DataTypes.STRING,
},
{
sequelize,
modelName: "Umkm_Kategori",
freezeTableName: true,
}
);
return Umkm_Kategori;
};
I have created some seeder data on postgres for both jenis and kategori table.
Now I want my getAllKategori API to actually include the value from "jenis" table using the relationship that I have made. Below is my apiController for kategori :
exports.findAllKategori = async (req, res) => {
//check if theres a filter applied
const nama = req.query.nama;
const condition = nama ? { nama: { [Op.iLike]: `%${nama}%` } } : null;
try {
const response = Kategori.findAll({
where: condition,
include: [{ model: db.jenis, as: "umkm_jenis", required: false }],
});
const data = await response;
return res.status(200).send(data);
} catch (error) {
console.log(error);
return res.status(500).send(error);
}
};
But when I tried to hit the API from POSTMAN, it shows SequelizeEagerLoadingError.
If I commented this line on kategoriController :
include: [{ model: db.jenis, as: "umkm_jenis", required: false }],
and then try to hit the api once more, it successfully shows all my data.
I have already read some similar question in this forum but none of it seems to work.
Thank you for taking your time!
UPDATE
Below is the 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 = 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;
db.kategori = require("./umkm_kategori")(sequelize, Sequelize);
db.jenis = require("./umkm_jenis")(sequelize, Sequelize);
db.produk = require("./umkm_produk")(sequelize, Sequelize);
db.review = require("./umkm_review")(sequelize, Sequelize);
module.exports = db;
console.log(error)
You already registered all models and associations with this code pieces:
const model = require(path.join(__dirname, file))(
sequelize,
Sequelize.DataTypes
);
db[model.name] = model;
and
Object.keys(db).forEach((modelName) => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
so this code is registering models once again, just remove it and use model names defined above.
db.kategori = require("./umkm_kategori")(sequelize, Sequelize);
db.jenis = require("./umkm_jenis")(sequelize, Sequelize);
db.produk = require("./umkm_produk")(sequelize, Sequelize);
db.review = require("./umkm_review")(sequelize, Sequelize);

Sequelize hasMany association not working

I have a Product model:
module.exports = (sequelize, Sequelize) => {
const Product = sequelize.define("product", {
SKU: {
type: Sequelize.STRING,
},
name: {
type: Sequelize.STRING,
},
description: {
type: Sequelize.TEXT,
},
categoryID: {
type: Sequelize.INTEGER,
}
});
Product.associate = function (models) {
Product.hasMany(models.Stock, { foreignKey: 'productID', as: 'stocks' })
};
return Product;
}
Which has a hasMany relation with the Stock model:
module.exports = (sequelize, DataTypes) => {
const Stock = sequelize.define("stock", {
note: DataTypes.STRING,
})
Stock.associate = function (models) {
Stock.belongsTo(models.Product)
};
return Stock;
}
When trying to query the Products, I get the following error:
exports.getAllAvailableProducts = (req, res) => {
const queryParams = req.query;
Product.findAll({
include: ['stocks'],
where: queryParams
}).then((products) => {
res.status(200).send(products);
})
};
(node:58108) UnhandledPromiseRejectionWarning: Error: Association with alias "stocks" does not exist on product
Am i doing something wrong here? I don't see the FK appearing in my database either
module.exports = (sequelize, Sequelize) => {
const Product = sequelize.define("product", {
SKU: {
type: Sequelize.STRING,
},
name: {
type: Sequelize.STRING,
},
description: {
type: Sequelize.TEXT,
},
categoryID: {
type: Sequelize.INTEGER,
}
});
Product.associate = function (models) {
// models.stock is correct
Product.hasMany(models.stock, { foreignKey: 'productID', as: 'stocks' })
};
return Product;
}
module.exports = (sequelize, DataTypes) => {
const Stock = sequelize.define("stock", {
note: DataTypes.STRING,
})
Stock.associate = function (models) {
// models.product is correct
//Stock.belongsTo(models.product)
Stock.belongsTo(models.product, {
foreignKey: 'productId',
as: 'product'
})
};
return Stock;
}
models/index.js
const dbConfig = require("../config/db.config.js");
const Sequelize = require("sequelize");
const sequelize = new Sequelize(dbConfig.DB, dbConfig.USER, dbConfig.PASSWORD, {
host: dbConfig.HOST,
dialect: dbConfig.dialect,
operatorsAliases: false,
pool: {
max: dbConfig.pool.max,
min: dbConfig.pool.min,
acquire: dbConfig.pool.acquire,
idle: dbConfig.pool.idle
}
});
const db = {};
db.Sequelize = Sequelize;
db.sequelize = sequelize;
db.user = require("./user.model.js")(sequelize, Sequelize);
db.category = require("./category.model.js")(sequelize, Sequelize);
db.product = require("./product.model.js")(sequelize, Sequelize);
db.orderline = require("./orderline.model.js")(sequelize, Sequelize);
db.order = require("./order.model.js")(sequelize, Sequelize);
db.stock = require("./stock.model.js")(sequelize, Sequelize);
// we need call associate function
db.stock.associate(db)
db.product.associate(db);
module.exports = db;
Product.findAll({
include: ['stocks'],
where: queryParams
}).then((products) => {
res.status(200).send(products);
})

Error: XModel is not associated to Ymodel on Sequelizejs

I have a few weeks working with Sequelize and I ran into this. I've already read other similar questions but nothing seems to help.
Let me show you my models:
Merchants.js
'use strict';
var Sequelize = require('sequelize');
var config = require(__base + '/server/config/config').provisioning;
var sequelize = new Sequelize(config.database, config.user, config.password, config);
var Merchants = sequelize.define('merchants', {
id: {
type: Sequelize.UUID,
primaryKey: true,
unique: true,
defaultValue: Sequelize.UUIDV4
},
name: {
type: Sequelize.STRING(255)
},
createdAt: {
type: Sequelize.DATE,
field: 'created_at'
},
//more attributes
}, {
classMethods: {
associate: function (models) {
Merchants.hasOne(models.Users, {foreignKey: 'merchantId'})
}
}
});
module.exports = Merchants;
Users.js
'use strict';
var Sequelize = require('sequelize');
var config = require(__base + '/server/config/config').provisioning;
var sequelize = new Sequelize(config.database, config.user, config.password, config);
var Users = sequelize.define('users', {
id: {
type: Sequelize.UUID,
primaryKey: true,
unique: true,
defaultValue: Sequelize.UUIDV4
},
merchantId: {
type: Sequelize.UUID,
field: 'merchant_id'
},
createdAt: {
type: Sequelize.DATE,
field: 'created_at'
},
// more attributes
}, {
classMethods: {
associate: function (models) {
Users.belongsTo(models.Merchants, { foreignKey: 'merchantId' });
}
}
});
module.exports = Users;
Then I'm trying to do this:
Users.findAll({
include: [{
model: Merchants,
where: { state: 'ACTIVATED' }
}]
}).then(function (users) {
console.log(users.length);
}).catch(function (err) {
console.log(err);
});
And I'm getting: [Error: merchants is not associated to users!]
I've done some of the stuff I've read in different questions but nothing seems to work. It might be a foolish thing, but as I said before, first time working with Sequelize.
Make sure that the associations are actually being set up - Do note that the associate method is not part of sequelize API, but is merely a common pattern - you have to make sure its actually called. For example with:
fs
.readdirSync(__dirname)
.filter(function(file) {
return (file.indexOf(".") !== 0) && (file !== "index.js");
})
.forEach(function(file) {
var model = sequelize.import(path.join(__dirname, file));
db[model.name] = model;
});
Object.keys(db).forEach(function(modelName) {
if ("associate" in db[modelName]) {
db[modelName].associate(db);
}
});
https://github.com/sequelize/express-example/blob/master/models/index.js

Resources