I am trying to implement Sequelize as an ORM in NodeJs and I am using it for Mysql,
I have 3 tables in the sample -
1. Role
2. User (Has a role)
3. Code (IsCreated by a user)
I'm unable to query the tables/models properly,
As I should be receiving an model representation of a table, which is referred as a foreign key.
Following is my DB structure -
1. Role table -
2. User table -
3. Code table -
Following are the table creation queries -
1. Role -
CREATE TABLE role (
id int(11) NOT NULL AUTO_INCREMENT,
name varchar(100) NOT NULL,
PRIMARY KEY (id)
);
2. User -
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
`email` varchar(100) NOT NULL,
`role` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `role` (`role`),
CONSTRAINT `user_ibfk_1` FOREIGN KEY (`role`) REFERENCES `role` (`id`)
);
3. Code -
CREATE TABLE `code` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
`createdBy` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `createdBy` (`createdBy`),
CONSTRAINT `code_ibfk_1` FOREIGN KEY (`createdBy`) REFERENCES `user` (`id`)
);
Following is my app.js file -
const db = require('./db');
const Code = require('./code');
const User = require('./user');
const Role = require('./role');
const async = require('async');
async.waterfall([
function(callback) {
db
.authenticate()
.then(() => {
console.log('Connection has been established successfully.');
callback(null,"ok");
})
.catch(err => {
return callback(err,null);
});
},
function(resp,callback) {
Code.findAll({include: [{ model: User}]})
.then(code => {
console.log("All users:", JSON.stringify(code, null, 4));
callback(null,"ok");
})
.catch(err => {
callback(err,null);
});
// Code.findOne({
// where: {
// id: 1
// }
// })
// .then(code => {
// console.log("All users:", JSON.stringify(code, null, 4));
// })
// .catch(err => console.log("Error => \n",err));
},
],
function(err, resp) {
if (err) {
console.log(err);
} else {
console.log(resp);
}
});
Following is my db.js file -
const Sequelize = require('sequelize');
module.exports = new Sequelize('junit', 'root', 'root', {
host: 'localhost',
/* one of 'mysql' | 'mariadb' | 'postgres' | 'mssql' */
dialect: 'mysql',
//operatorsAliases: false,
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000
}
});
Following is my role.js file -
const Sequelize = require('sequelize');
const db = require('./db');
const User = require('./user');
const Role = db.define('role', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
allowNull:false,
autoIncrement: true
},
name: {
type: Sequelize.STRING
}
}, {
tableName: 'role',
freezeTableName: true,
timestamps: false
});
associate : (models) => {
Role.hasMany(models.User,{
foreignKey: 'role'
});
};
module.exports = Role;
Following is my user.js file -
const Sequelize = require('sequelize');
const db = require('./db');
const Code = require('./code');
const Role = require('./role');
const User = db.define('user', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
allowNull:false,
autoIncrement: true
},
name: {
type: Sequelize.STRING
},
email: {
type: Sequelize.STRING
},
role: {
type: Sequelize.INTEGER,
references: {
// This is a reference to another model
model: Role,
// This is the column name of the referenced model
key: 'id'
}
}
}, {
tableName: 'user',
freezeTableName: true,
timestamps: false
});
associate : (models) => {
User.hasMany(models.Code,{
foreignKey: 'createdBy'
});
User.belongsTo(models.Role,{
foreignKey: 'role'
});
};
module.exports = User;
Following is my code.js file -
const Sequelize = require('sequelize');
const db = require('./db');
const User = require('./user');
//one-to-many
const Code = db.define('code', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
allowNull:false,
autoIncrement: true
},
name: {
type: Sequelize.STRING
},
// createdBy: {
// type: Sequelize.INTEGER
// },
createdBy: {
type: Sequelize.INTEGER,
references: {
// This is a reference to another model
model: User,
// This is the column name of the referenced model
key: 'id'
}
}
}, {
tableName: 'code',
freezeTableName: true,
timestamps: false
});
associate : (models) => {
Code.belongsTo(models.User,{
foreignKey: 'createdBy'
});
};
module.exports = Code;
When I run the app.js file I can't see the model reference of User,
But I get the usual Integer value, Can someone please help on how to properly use the Model here?
Error trace -
Looks like you're trying to fetch Code when connection wasn't established yet.
Try this:
const db = require('./db');
const Code = require('./code');
const User = require('./user');
const Role = require('./role');
function run() {
return db
.authenticate()
.then(() => Code.findOne({ // you can try execute whenever query you want here
where: {
id: 1
}})
.then(code => {
console.log("All users:", JSON.stringify(code, null, 4));
})
.catch(err => console.log("Error => \n",err)))
.catch(err => {
console.error('Unable to connect to the database:', err);
});
}
run();
Related
I am trying to figure out how I can populate the foreign key in my child table(subTaskTickets) from the parent table (Tickets).
When i create a Ticket, then I create a subTaskTicket, the ticketID and ETR_ID remain null and dont populate with the id from Ticket table.
Not sure how I can do this as i am new with sequelize, nodejs and mysql.
Here are my 2 modals:
SubTaskTicket model (child)
const Sequelize = require("sequelize-v5");
const sequelize = require("../connection");
//create a new Date object with the current date and time
const date = new Date();
//extract the year and month from the date
const year = date.getFullYear();
const month = date.getMonth() + 1;
//combine the year and month into a single string to be concat with id to form ETR_ID
const yearMonth = year.toString().concat("-", month.toString(), "-ST");
const SubTaskTicket = sequelize.define("subTaskTicket", {
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true
},
ETR_ID: {
type: Sequelize.STRING,
// primaryKey: true,
// defaultValue: ""
allowNull: true,
references: {
model: 'tickets',
key: 'ETR_ID'
}
},
subTaskId: {
type: Sequelize.STRING,
allowNull: false,
unique: true,
defaultValue: "",
primaryKey: true
},
Title: {
type: Sequelize.STRING,
allowNull: false
},
Description: {
type: Sequelize.STRING,
allowNull: false
},
Status: {
type: Sequelize.CHAR,
allowNull: false
},
ETR: {
type: Sequelize.STRING,
allowNull: false,
defaultValue: yearMonth
},
});
module.exports = SubTaskTicket;
Ticket model (Parent)
const Sequelize = require('sequelize-v5');
const sequelize = require('../connection');
//create a new Date object with the current date and time
const date = new Date();
//extract the year and month from the date
const year = date.getFullYear();
const month = date.getMonth()+1;
//combine the year and month into a single string to be concat with id to form ETR_ID
const yearMonth = year.toString().concat('-', month.toString(),'-');
const Tickets = sequelize.define('tickets', {
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true
},
ETR_CAT:{
type: Sequelize.STRING,
allowNull: false
},
ETR_ID: {
type: Sequelize.STRING,
primaryKey: true,
unique: true,
defaultValue: ""
},
Title: {
type: Sequelize.STRING,
allowNull: false
},
Description:{
type: Sequelize.STRING,
allowNull: false
},
ETR: {
type: Sequelize.STRING,
allowNull: false,
defaultValue: yearMonth
}
});
module.exports = Tickets;
Here is my app.js file containing express etc:
const express = require("express");
const cors = require("cors");
const app = express();
const sequelize = require("./connection");
const Tickets = require("./models/ticket.model");
const SubTaskTicket = require('./models/subTaskTicket.model');
var corsOptions = {
origin: "http://localhost:8081"
};
app.use(cors(corsOptions));
// parse requests of content-type - application/json
app.use(express.json());
// parse requests of content-type - application/x-www-form-urlencoded
app.use(express.urlencoded({ extended: true }));
// simple route
app.get("/", (req, res) => {
res.json({ message: "Welcome to Tylers application." });
});
require("./routes/ticket.routes")(app);
Tickets.hasMany(SubTaskTicket, {
as: 'subtaskticket'
});
SubTaskTicket.belongsTo(Tickets);
Tickets.hasMany(SubTaskTicket);
//will create tables from our modals, but also define relations in our DB
// sync() command for dev, add { force: true } so i can remake tables from scratch right away
sequelize.sync().then(result => {
console.log(result);
// set port, listen for requests
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}.`);
});
})
.catch(err => {
console.log(err);
});
Next are my 2 controllers for each model:
subTaskTicket controller
const Sequelize = require("sequelize-v5");
const sequelize = require("../connection");
const subTaskTicket = require("../models/subTaskTicket.model");
//Sequelized create format
exports.createTicket = (req, res, next) => {
const Title = req.body.Title;
const Description = req.body.Description;
const Status = req.body.Status;
// const ETR = req.body.ETR;
subTaskTicket.create({
Title: Title,
Status: Status,
Description: Description,
//ETR: ETR
})
.then(result => {
//console.log(result);
console.log("Created Ticket");
sequelize.query('update subtasktickets set subTaskId = concat(ETR,id)');
})
.catch(err => {
console.log(err);
})
}
//Sequelized findAll
exports.findAllTickets = (req, res, next) => {
subTaskTicket.findAll({include: ["subtaskticket"]})
.then(data => {
res.send(data);
}).catch(err => {
console.log(err);
});
}
// Sequelized Find a single Tutorial with a id
exports.findOneTicket = (req, res) => {
const id = req.params.id;
subTaskTicket.findByPk(id)
.then(data => {
if (data) {
res.send(data);
} else {
res.status(404).send({
message: 'Cannot find Child Ticket with id = ' + id
});
}
})
.catch(err => {
res.status(500).send({
message: 'Error retrieving child Ticket with id= ' + id
});
});
};
//Sequilized Update Ticket identified by the id in the req
exports.updateTicket = (req, res) => {
const id = req.params.id;
subTaskTicket.update(req.body, {
where: { id: id }
})
.then(num => {
if (num == 1) {
res.send({
message: "Child Ticket was updated successfully."
});
} else {
res.send({
message: `Cannot update Child Ticket with id=${id}. Maybe Child Ticket was not found or req.body is empty!`
});
}
})
.catch(err => {
res.status(500).send({
message: "Error updating Ticket with id=" + id
});
});
};
// Sequilized Delete a Ticket with the specified id in the request
exports.deleteTicket = (req, res) => {
const id = req.params.id;
subTaskTicket.destroy({
where: { id: id }
})
.then(num => {
if (num == 1) {
res.send({
message: "Child Ticket was deleted successfully!"
});
} else {
res.send({
message: `Cannot delete Child Ticket with id= ${id}. Maybe Child Ticket was not found!`
});
}
})
.catch(err => {
res.status(500).send({
message: "Could not delete Ticket with id=" + id
});
});
};
ticket.controller
const Sequelize = require("sequelize-v5");
const sequelize = require("../connection");
const Tickets = require("../models/ticket.model");
const { Op } = require("sequelize-v5");
//Sequelized create format
exports.createTicket = (req, res, next) => {
const ETR_CAT = req.body.ETR_CAT;
const Title = req.body.Title;
const Description = req.body.Description;
const ticketId = req.body.ticketId
Tickets.create({
ETR_CAT: ETR_CAT,
Title: Title,
Description: Description,
})
.then(result => {
//console.log(result);
console.log("Created Ticket");
sequelize.query('update tickets set ETR_ID = concat(ETR,id)');
})
.catch(err => {
console.log(err);
})
}
//Sequelized findAll
exports.findAllTickets = (req, res, next) => {
Tickets.findAll({include: ["subtaskticket"]})
.then(data => {
res.send(data);
}).catch(err => {
console.log(err);
});
}
// Sequelized Find a single Tutorial with a id
exports.findOneTicket = (req, res) => {
const id = req.params.id;
Tickets.findByPk(id, {include: ["subtaskticket"]})
.then(data => {
if (data) {
res.send(data);
} else {
res.status(404).send({
message: 'Cannot find Ticket with id = ' + id
});
}
})
.catch(err => {
res.status(500).send({
message: 'Error retrieving Ticket with id= ' + id
});
});
};
//Sequilized Update Ticket identified by the id in the req
exports.updateTicket = (req, res) => {
const id = req.params.id;
Tickets.update(req.body, {
where: { id: id }
})
.then(num => {
if (num == 1) {
res.send({
message: "Ticket was updated successfully."
});
} else {
res.send({
message: `Cannot update Ticket with id=${id}. Maybe Ticket was not found or req.body is empty!`
});
}
})
.catch(err => {
res.status(500).send({
message: "Error updating Ticket with id=" + id
});
});
};
// Sequilized Delete a Ticket with the specified id in the request
exports.deleteTicket = (req, res) => {
const id = req.params.id;
Tickets.destroy({
where: { id: id }
})
.then(num => {
if (num == 1) {
res.send({
message: "Ticket was deleted successfully!"
});
} else {
res.send({
message: `Cannot delete Ticket with id= ${id}. Maybe Ticket was not found!`
});
}
})
.catch(err => {
res.status(500).send({
message: "Could not delete Ticket with id=" + id
});
});
};
and this is my routes:
module.exports = app => {
const tickets = require("../controllers/ticket.controller");
const subTaskTicket = require("../controllers/subTaskTicket.controller")
var router = require("express").Router();
// Create a new Tutorial
router.post("/addTicket", tickets.createTicket);
router.post("/addSubTicket", subTaskTicket.createTicket);
// Retrieve all Tickets
router.get("/allTickets", tickets.findAllTickets);
// Retrieve a single Ticket with id
router.get("/ticket/:id", tickets.findOneTicket);
// Update a Ticket with id
router.put("/updateTicket/:id", tickets.updateTicket);
// Delete a Ticket with id
router.delete("/deleteTicket/:id", tickets.deleteTicket);
// // Delete all Tickets
// router.delete("/", tickets.deleteAll);
app.use("/api/tickets", router);
};
Any ideas on what i am missing or how I can create a row in the subTaskTicket table that would populate the foreign keys with the proper id?
when i integrate this with a frontend (angular) I want Ticket to be the main ticket (ex has id =1) and then i can create "sub tickets" that would be tasks under that main ticket. So they would populate the subtaskticket table and be tied into that parent Ticket with the id = 1.
Any help would be appreciated! as i am stuck and trying to figure this out while using sequelize
Below is the query that is generated when i run my nodemon command. This is using sequelize.sync() in app.js to make the tables when i first create it.
Executing (default): CREATE TABLE IF NOT EXISTS `tickets` (`id` INTEGER NOT NULL auto_increment , `ETR_CAT` VARCHAR(255) NOT NULL, `ETR_ID` VARCHAR(255) DEFAULT '' UNIQUE , `Title` VARCHAR(255) NOT NULL, `Description` VARCHAR(255) NOT NULL, `ETR` VARCHAR(255) NOT NULL DEFAULT '2022-12-', `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, PRIMARY KEY (`id`, `ETR_ID`)) ENGINE=InnoDB;
Executing (default): SHOW INDEX FROM `tickets`
Executing (default): CREATE TABLE IF NOT EXISTS `subTaskTickets` (`id` INTEGER NOT NULL auto_increment , `ETR_ID` VARCHAR(255), `subTaskId` VARCHAR(255) NOT NULL DEFAULT '' UNIQUE , `Title` VARCHAR(255) NOT NULL, `Description` VARCHAR(255) NOT NULL, `Status` CHAR(255) NOT NULL, `ETR` VARCHAR(255) NOT NULL DEFAULT '2022-12-ST', `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, `ticketId` INTEGER, PRIMARY KEY (`id`, `subTaskId`), FOREIGN KEY (`ETR_ID`) REFERENCES `tickets` (`ETR_ID`)) ENGINE=InnoDB;
Executing (default): SHOW INDEX FROM `subTaskTickets`
You need to choose only one auto-generated column as a primary key in both models
You need to indicate a foreign key column in associations explicitly (and the same for both paired associations) since you use the non-default name for it:
Tickets.hasMany(SubTaskTicket, {
foreignKey: 'ETR_ID'
});
SubTaskTicket.belongsTo(Tickets, {
foreignKey: 'ETR_ID'
});
I know this is a general question, but I have the following code in my app.js
app.post('/data', (req, res) =>{
const file = req.files.filename;
const nose = [];
const filename = file.name;
file.mv('./excel/'+filename, (err)=>{
if(err){
console.log(err)
} else{
const result = importExcel({
sourceFile: './excel/'+filename,
header: {rows:1},
columnToKey :{A:'Dimension', B:'Category', C:'Subcategory',D:'Factor', E:'Context', F:'Date',G:'Indicator', H:'Formula',I:'FoundValue'},
sheets:['data']
})
for(var i=0; result.data.length > i; i++){
nose.push(result.data[i].Dimension,
result.data[i].Category)
}
res.send(nose)
console.log(nose+' = Total data '+nose.length);
}
})
})
Here is my data.ejs in the part of the button for sending data
<form action="/data" method="post" enctype="multipart/form-data">
<div style="float:right">
<input style="margin-top:6%;" class="btn btn-light" type="file" name="filename">
<button style="float:right" type="submit" class="btn btn-success mt-4"><i class="fa-solid fa-file-import"></i></button>
</div>
<div class="footer" style="margin-left: 0px; margin-right: 0px!important;">
Right now it looks visually like this;
And my data is displayed like this;
Which is fine, because that's what my excel looks like;
Context: I need to send that .json data in app.js to my MySQL database using Nodejs but I don't know how to send that data to my database table, I've seen that they use multer,sequelize, etc. I don't know if that's the way or there are easier ways to understand for a newbie like me.
I also have an app. get, like this;
app.get('/data', (req, res) =>{
connection.query('SELECT c.data_id, d.dimension_name, cc.category_name, s.subcategory_name, f.factor_name,f.factor_description,c.date_creation_data, c.Indicator,ff.formula_name FROM data_load c INNER JOIN dimensions d ON c.dimension_id = d. id_dimensions INNER JOIN categories cc ON c.id_categories = cc.id_categories INNER JOIN subcategory s ON c.id_subcategories = s.id_subcategories INNER JOIN factors f ON c.id_factor = f.id_factor INNER JOIN formulas ff ON c.id_formula = ff.id_formula;',
function(error, results){
if(error){
console.log(error)
} else{
res.render('./data', {
results:results
})
}
})
})
My table, too, as you noticed in app.get has several INNER JOIN, so this is my table at present;
CREATE TABLE `carga_datos` (
`id_datos` int(11) NOT NULL,
`id_dimensiones` int(11) DEFAULT NULL,
`id_categorias` int(11) DEFAULT NULL,
`id_subcategorias` int(11) DEFAULT NULL,
`id_factor` int(11) DEFAULT NULL,
`fecha_creacion_data` timestamp NOT NULL DEFAULT current_timestamp(),
`Indicador` int(11) NOT NULL,
`id_formula` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
ALTER TABLE `carga_datos`
ADD PRIMARY KEY (`id_datos`),
ADD KEY `id_dimensiones` (`id_dimensiones`),
ADD KEY `id_categorias` (`id_categorias`),
ADD KEY `id_subcategorias` (`id_subcategorias`),
ADD KEY `id_factor` (`id_factor`),
ADD KEY `id_formula` (`id_formula`);
ALTER TABLE `carga_datos`
MODIFY `id_datos` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2;
ALTER TABLE `carga_datos`
ADD CONSTRAINT `carga_datos_ibfk_1` FOREIGN KEY (`id_dimensiones`) REFERENCES `dimensiones` (`id_dimensiones`),
ADD CONSTRAINT `carga_datos_ibfk_2` FOREIGN KEY (`id_categorias`) REFERENCES `categorias` (`id_categorias`),
ADD CONSTRAINT `carga_datos_ibfk_3` FOREIGN KEY (`id_subcategorias`) REFERENCES `subcategoria` (`id_subcategorias`),
ADD CONSTRAINT `carga_datos_ibfk_4` FOREIGN KEY (`id_factor`) REFERENCES `factores` (`id_factor`),
ADD CONSTRAINT `carga_datos_ibfk_5` FOREIGN KEY (`id_formula`) REFERENCES `formulas` (`id_formula`);
COMMIT;
Please help, I don't know what to do,
Thanks in advance :(
I suggest using sequelize for your data models. This example should get you on your way.
You create and update tables via migrations, then run them via your console:
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('carga_datos', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
id_datos: {
allowNull: false,
type: Sequelize.INTEGER
},
id_dimensiones: {
allowNull: true,
type: Sequelize.INTEGER
},
id_categorias: {
allowNull: true,
type: Sequelize.INTEGER
},
id_subcategorias: {
allowNull: true,
type: Sequelize.INTEGER
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
})
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('carga_datos');
}
};
You will define your model schemas like this:
'use strict';
module.exports = (sequelize, DataTypes) => {
var carga_datos = sequelize.define('carga_datos', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: DataTypes.INTEGER
},
id_datos: {
allowNull: false,
type: DataTypes.INTEGER
},
id_dimensiones: {
allowNull: true,
type: DataTypes.INTEGER
},
id_categorias: {
allowNull: true,
type: DataTypes.INTEGER
},
id_subcategorias: {
allowNull: true,
type: DataTypes.INTEGER
},
createdAt: {
allowNull: false,
type: DataTypes.DATE
},
updatedAt: {
allowNull: false,
type: DataTypes.DATE
}
})
carga_datos.associate = function (models) {
}
return carga_datos;
}
When you want to create a new entry in your database, you will use:
const datos = await carga_datos.create({
id_datos: req.body.id_datos,
id_dimensiones: req.body.id_dimensiones
})
Pay attention to the types when writing models and migrations, they may get confused. Good luck!
I am using the beforeCreate to encrypt password before saving to database.
When I do:
const user = await User.create({name, email, password});
res.json(user);
I see the encrypted password in response. But in the database the password is not encrypted. If I do user.reload() and then send, I see what's stored in the database(unencrypted password).
This is the model:
User.init({
name: {
type: DataTypes.STRING,
allowNull: false
},
...
},{
sequelize,
hooks: {
beforeCreate: (user, options) => {
return bcrypt.genSalt(10)
.then(salt => {
bcrypt.hash(user.password, salt)
.then(hashedPassword => {
user.password = hashedPassword;
console.log(user.password, "FS");
return user.password;
})
.catch(err => console.log(err));
})
.catch(err => console.log(err));
}
}
})
This is the controller:
try{
const {name, email, password} = req.body;
if(isEmpty(name) || isEmpty(email) || isEmpty(password)){
res.status(400).json({errMessage: 'Enter name, email and password'});
}
const user = await User.create({name, email, password});
res.json(user); //data with encrypted password is sent, but not saved in db
}
The beforeCreate hook does not need to return a value, the return value type of the function signature as follows:
export type HookReturn = Promise<void> | void;
Besides, you forgot to add return before bcrypt.hash(user.password, salt) statement causes the beforeCreate function not to wait for the encryption asynchronous operation to complete.
Here is a working example:
import { sequelize } from '../../db';
import { Model, DataTypes } from 'sequelize';
import bcrypt from 'bcrypt';
class User extends Model {
password!: string;
name!: string;
}
User.init(
{
name: {
type: DataTypes.STRING,
allowNull: false,
},
email: {
type: DataTypes.STRING,
allowNull: false,
},
password: {
type: DataTypes.STRING,
allowNull: false,
},
},
{
sequelize,
modelName: 'users',
hooks: {
beforeCreate: (user: User) => {
return bcrypt
.genSalt(10)
.then((salt) => {
return bcrypt
.hash(user.password, salt)
.then((hashedPassword) => {
user.password = hashedPassword;
})
.catch((err) => console.log(err));
})
.catch((err) => console.log(err));
},
},
},
);
(async function() {
try {
await sequelize.sync({ force: true });
await User.create({ name: 'ab', email: 'test#gmail.com', password: '123456' });
} catch (error) {
console.log(error);
} finally {
await sequelize.close();
}
})();
The execution log:
Executing (default): DROP TABLE IF EXISTS "users" CASCADE;
Executing (default): DROP TABLE IF EXISTS "users" CASCADE;
Executing (default): CREATE TABLE IF NOT EXISTS "users" ("id" SERIAL , "name" VARCHAR(255) NOT NULL, "email" VARCHAR(255) NOT NULL, "password" VARCHAR(255) NOT NULL, PRIMARY KEY ("id"));
Executing (default): SELECT i.relname AS name, ix.indisprimary AS primary, ix.indisunique AS unique, ix.indkey AS indkey, array_agg(a.attnum) as column_indexes, array_agg(a.attname) AS column_names, pg_get_indexdef(ix.indexrelid) AS definition FROM pg_class t, pg_class i, pg_index ix, pg_attribute a WHERE t.oid = ix.indrelid AND i.oid = ix.indexrelid AND a.attrelid = t.oid AND t.relkind = 'r' and t.relname = 'users' GROUP BY i.relname, ix.indexrelid, ix.indisprimary, ix.indisunique, ix.indkey ORDER BY i.relname;
Executing (default): INSERT INTO "users" ("id","name","email","password") VALUES (DEFAULT,$1,$2,$3) RETURNING *;
Check data record in the database:
node-sequelize-examples=# select * from users;
id | name | email | password
----+------+----------------+--------------------------------------------------------------
1 | ab | test#gmail.com | $2b$10$XQb89m.b6ie8ImokS6JPdurWfIH4Cq19y.XGhb7LpWYUklp5jaYh2
(1 row)
I am testing all the associations with sequelize and I am getting a problem to get joins for many to many with belongsToMany.
https://github.com/lhferrh/Sequelize-Playground.git
When I do the findAll through I get the all null for the join result
I have been checking several combinations but the thing is that the SQL produced by sequelize is working on MySQL directly and it makes me really confused.
This is the sql provided:
SELECT `user`.`userId`, `user`.`name`, `user`.`createdAt`, `user`.`updatedAt`, `cars`.`carId` AS `cars.carId`, `cars`.`make` AS `cars.make`, `cars`.`createdAt` AS `cars.createdAt`, `cars`.`updatedAt` AS `cars.updatedAt`, `cars->favorites`.`favoritesId` AS `cars.favorites.favoritesId`, `cars->favorites`.`date` AS `cars.favorites.date`, `cars->favorites`.`createdAt` AS `cars.favorites.createdAt`, `cars->favorites`.`updatedAt` AS `cars.favorites.updatedAt`, `cars->favorites`.`userId` AS `cars.favorites.userId`, `cars->favorites`.`carId` AS `cars.favorites.carId` FROM `users` AS `user` LEFT OUTER JOIN ( `favorites` AS `cars->favorites` INNER JOIN `cars` AS `cars` ON `cars`.`carId` = `cars->favorites`.`carId`) ON `user`.`userId` = `cars->favorites`.`userId`;
This is the code I am running:
const Users = sequelize.define('user', {
userId: {
type: Sequelize.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true
},
name: {
type: Sequelize.STRING,
},
}
);
const Favorites = sequelize.define('favorites', {
favoritesId: {
type: Sequelize.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true
},
date: {
type: Sequelize.DATE,
}
}
);
const Cars = sequelize.define('cars', {
carId: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
make: Sequelize.STRING
})
Users.belongsToMany(Cars, {
through: 'favorites',
sourceKey: 'userId',
foreignKey: 'userId'
});
Cars.belongsToMany(Users, {
through: 'favorites',
sourceKey: 'carId',
foreignKey: 'carId'
});
const intiDataBase = async () => {
await sequelize.sync({force: true});
}
const run = async () => {
const max = 3;
await intiDataBase();
await Promise.all( DATA.users.map( async elem=>
Users.create({...elem})
))
await Promise.all( DATA.cars.map( async elem =>
Cars.create({...elem})
))
for( let i = 0 ; i < 5 ; i++ ){
Favorites.create({
userId: getRandomInt(1 , max),
carId: getRandomInt(0, max)
})
}
const car = await Users.findAll({
include: [{
model: Cars,
through: {
attributes: ['userId', 'carId'],
}
//attributes: ['make'],
}],
raw: true
});
const favorites = await Favorites.findAll({
where:{
userId:2
},
raw: true
});
console.log(car);
console.log(favorites);
}
run();
This is result I get:
name: 'Johan',
createdAt: 2019-10-05T08:57:57.000Z,
updatedAt: 2019-10-05T08:57:57.000Z,
'cars.carId': null,
'cars.make': null,
'cars.createdAt': null,
'cars.updatedAt': null,
'cars.favorites.favoritesId': null,
'cars.favorites.date': null,
'cars.favorites.createdAt': null,
'cars.favorites.updatedAt': null,
'cars.favorites.userId': null,
'cars.favorites.carId': null } ], ...
Probably this is a naming problem but the fact the SQL is working directly makes is really confusing.
I hope any of you can see the error.
By the way, I was also wondering what would be the disadvantage of creating the many to many relationship manually by creating two 1:m associations to the intermediate table?
I found my error. There is an await missing when I am inserting favorites to the database. For that reason the join was giving the the wrong result in the query and the correct one in the database directly.
for( let i = 0 ; i < 5 ; i++ ){
await Favorites.create({
userId: getRandomInt(1 , max),
carId: getRandomInt(0, max)
})
}
As I was expecting, a silly error.
Anyways I am still hesitant about using belongToMany or creating the many to many relationship by myself and use always two steps.
For an existing MySQL database, I used Sequelize-auto package to generate the models. But the associations don't come with model classes.
I have an MySQL database and I'm using it for NodeJS web project. Also I'm using Sequelize as the ORM. Since database is already there I wanted to generate the model classes as the entities. So I used sequelize-auto
https://github.com/sequelize/sequelize-auto to generate the model classes. But when they were generated attributes have been correctly set but the associations doesn't come up with the model classes. So I have faced problem when fetching the data from the database.
Here are the two model classes that were generated with sequlize-auto. There are two table in the database named as department and category. department.js and category.js are the two model classes that were generated
department.js
module.exports = function(sequelize, DataTypes) {
return sequelize.define('department', {
department_id: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
autoIncrement: true
},
name: {
type: DataTypes.STRING(100),
allowNull: false
},
description: {
type: DataTypes.STRING(1000),
allowNull: true
}
}, {
tableName: 'department',
timestamps: false,
});
};
category.js
module.exports = function(sequelize, DataTypes) {
return sequelize.define('category', {
category_id: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
autoIncrement: true
},
department_id: {
type: DataTypes.INTEGER(11),
allowNull: false
},
name: {
type: DataTypes.STRING(100),
allowNull: false
},
description: {
type: DataTypes.STRING(1000),
allowNull: true
}
}, {
tableName: 'category',
timestamps: false,
});
};
So what else needs to be done in order to get the associations and to fetch data successfully. Can some one help me here. Table structure is as following.
1) inside of your models folder create an index.js file and add the following code
import Sequelize from 'sequelize';
const fs = require('fs');
const path = require('path');
const basename = path.basename(__filename);
const db = {};
// #ts-ignore
const sequelize = new Sequelize('dbname', 'dbUser', 'password', {
host: '127.0.0.1',
port: 'PORT',
dialect: 'mysql',
define: {
freezeTableName: true,
timestamps: false,
},
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000,
},
// <http://docs.sequelizejs.com/manual/tutorial/querying.html#operators>
operatorsAliases: false,
});
const tableModel = {};
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;
tableModel[model.name] = model;
});
Object.getOwnPropertyNames(db).forEach(modelName => {
const currentModel = db[modelName];
Object.getOwnPropertyNames(currentModel.rawAttributes).forEach(attributeName => {
if (
Object.prototype.hasOwnProperty.call(
currentModel.rawAttributes[attributeName],
'references'
) &&
Object.prototype.hasOwnProperty.call(
currentModel.rawAttributes[attributeName].references,
'model'
) &&
Object.prototype.hasOwnProperty.call(
currentModel.rawAttributes[attributeName].references,
'key'
)
) {
if (
!(
currentModel.rawAttributes[attributeName].references.model &&
currentModel.rawAttributes[attributeName].references.key
)
) {
console.log(
`*SKIPPED* ${modelName} ${attributeName} references a model ${currentModel.rawAttributes[attributeName].references.model} with key ${currentModel.rawAttributes[attributeName].references.key}`
);
return;
}
console.log(
`${modelName} ${attributeName} references a model ${currentModel.rawAttributes[attributeName].references.model} with key ${currentModel.rawAttributes[attributeName].references.key}`
);
const referencedTable =
tableModel[currentModel.rawAttributes[attributeName].references.model];
currentModel.belongsTo(referencedTable, { foreignKey: attributeName });
referencedTable.hasMany(currentModel, { foreignKey: attributeName });
}
});
});
// #ts-ignore
db.sequelize = sequelize;
// #ts-ignore
db.Sequelize = Sequelize;
// eslint-disable-next-line eol-last
module.exports = db;
2) inside of your resolver just reference the above:
const db = require('../assets/models/index');
To add to #CodingLittles answer which he based from here.
I added the following for making many to many assosiations:
enum Junctions {
user = 'user',
roles = 'roles',
}
enum JuntiontThrough {
userroles = 'userroles',
}
interface JunctionObject {
junctionBelongsTo?: any;
}
const checkIfAttrExists= (obj, value) => {
return Object.prototype.hasOwnProperty.call(obj, value);
};
const checkRefrence = (obj, attr, value) => {
return obj.rawAttributes[attr].references[value];
};
export const getJunction = (junc: Junctions): JunctionObject => {
const junctions = {
user: {
junctionBelongsTo: [
{ key: Junctions.roles, value: juntiontThrough.userroles }
],
},
roles: {
junctionBelongsTo: [{ key: Junctions.user, value: juntiontThrough.userroles }],
},
}[junc];
if (!junctions) return {};
return junctions;
};
const models = Object.getOwnPropertyNames(db);
models.forEach(modelName => {
const currentModel = db[modelName];
const junction = getJunction(modelName as Junctions);
if (!_.isEmpty(junction)) {
// eslint-disable-next-line array-callback-return
junction.junctionBelongsTo.reduce((key, value) => {
currentModel.belongsToMany(db[value.key], {
through: db[value.value],
});
}, {});
}
const attributes = Object.getOwnPropertyNames(currentModel.rawAttributes);
attributes.forEach(attributeName => {
if (
checkIfAttrExists(currentModel.rawAttributes[attributeName], 'references') &&
checkIfAttrExists(currentModel.rawAttributes[attributeName].references, 'model') &&
checkIfAttrExists(currentModel.rawAttributes[attributeName].references, 'key')
) {
if (
!(
checkRefrence(currentModel, attributeName, 'model') &&
checkRefrence(currentModel, attributeName, 'key')
)
) {
return;
}
const referencedTable =
tableModel[currentModel.rawAttributes[attributeName].references.model];
if (!(modelName.toString() in juntiontThrough)) {
console.log(
`${modelName} ${attributeName} references a model ${currentModel.rawAttributes[attributeName].references.model} with key ${currentModel.rawAttributes[attributeName].references.key}`
);
currentModel.belongsTo(referencedTable, { foreignKey: attributeName });
referencedTable.hasMany(currentModel, { foreignKey: attributeName });
}
}
});
})
Note that for the many to many relations to work you have to add the relations manually like I did in the getJunction function
I followed the pattern demonstrated in the typescript example on sequelize-auto (init-models.ts) - within the existing initModels function, works fine in js.
export function initModels(sequelize: Sequelize) {
Product.initModel(sequelize);
Supplier.initModel(sequelize);
Supplier.hasMany(Product, { as: "products", foreignKey: "supplierId"});
return {...