NodeJs Join two tables using sequalize and retrieve the data in Controller - node.js

I have two model classes in separate files as below,
module.exports = (sequelize, Sequelize) => {
return sequelize.define(
"course",
{
id: {
type: Sequelize.STRING,
primaryKey: true,
field: 'ID',
},
title: {
type: Sequelize.STRING,
field: 'TITLE'
}
},
{
timestamps: false,
freezeTableName: true,
underscored: true
},
);
};
module.exports = (sequelize, Sequelize) => {
return sequelize.define(
"student",
{
id: {
type: Sequelize.STRING,
primaryKey: true,
field: 'ID',
},
courseId: {
type: Sequelize.STRING,
field: 'COURSE_ID'
}
},
{
timestamps: false,
freezeTableName: true,
underscored: true
},
);
};
And in the controller I have written like this.
const db = require("../config/sequelize.config");
const course = db.course;
const student= db.student;
student.belongsTo(course, {foreignKey: 'courseId', targetKey: 'id'});
exports.findStudentData = (req, res) => {
return student.findOne({ limit: 1 },
{
include : [{
model: course
}]}).then(data => {
res.send(data);
}).
catch(err => {
res.status(500).send({
message:
err.message || "Some error occurred."
});
});
};
Here I need to get course data along with student data. When I ran the code it only gives me the student data without course details. I'm not sure If I have added the following statement correct
student.belongsTo(course, {foreignKey: 'courseId', targetKey: 'id'});

Related

Pulling associations values with Sequelize

I am using sequelize for the first time and I having trouble getting my head around the association / foreign key relationships when pulling data into my jsons. I have some experience with SQL joins when I used PHP and I want to display the values that relate to the integer values in my diveSchool model.
Is there an easy way to do this without creating a complicated API?
The integers with ID relate to other tables and I have the foreign keys already created in pgadmin. Obviously the integers will mean nothing by themselves when displayed on the front-end.
module.exports = (sequelize, Sequelize) => {
const diveSchool = sequelize.define("diveSchools", {
diveSchoolID: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false
},
diveSchoolName: {
type: Sequelize.TEXT
},
diveSchoolLocation: {
type: Sequelize.TEXT
},
diveSchoolRegionID: {
type: Sequelize.INTEGER
},
diveCertificatesOfferedID: {
type: Sequelize.INTEGER
},
diveSpotsOfferedID: {
type: Sequelize.INTEGER
},
diveSchoolGeo: {
type: Sequelize.GEOMETRY('POINT'),
allowNull: false
},
},
{
timestamps: false
}, {});
diveSchool.associate = function(models){
diveSchool.belongsTo(models.diveRegion, {foreignKey: 'diveSchoolRegionID', as: 'diveRegion'})
diveSchool.belongsTo(models.diveCertification, {foreignKey: 'diveCertification', as: 'diveCertification'})
diveSchool.belongsTo(models.diveSpot, {foreignKey: 'diveSpotID', as: 'diveSpot'})
};
return diveSchool;
};
diveRegion.js
module.exports = (sequelize, Sequelize) => {
const diveRegion = sequelize.define("diveRegions", {
diveRegionID: {
type: Sequelize.INTEGER,
primaryKey: true
},
diveRegion: {
type: Sequelize.TEXT
}},
{
timestamps: false
},{});
diveRegion.associate = function(models){
diveRegion.hasMany(models.diveSchool, {as: 'diveRegion'})
};
return diveRegion;
};
diveCertifications.js
module.exports = (sequelize, Sequelize) => {
const diveCertification = sequelize.define("diveCertifications", {
diveCertificationID: {
type: Sequelize.INTEGER,
primaryKey: true
},
diveCertificationName: {
type: Sequelize.TEXT
}},
{
timestamps: false
},{});
diveCertification.associate = function(models){
diveCertification.hasMany(models.diveSchool, {as: 'diveCertificatesOfferedID'})
diveCertification.hasMany(models.diveCertsCompleted, {as: 'diveCertsCompletedID'})
};
return diveCertification;
};
diveSchool.controller.js API
exports.allDiveSchools = (req, res) => {
approvedDivingSchool.findAll({})
.then((approvedDivingSchool) => {
const diveSchoolsList = [];
for (i = 0; i < approvedDivingSchool.length; i++) {
diveSchoolsList.push(approvedDivingSchool[i].dataValues);
}
if (!approvedDivingSchool) {
return res.status(404).send({ message: "No dive schools stored in this region." });
}
res.status(200).send({
data: diveSchoolsList,
});
})
.catch((err) => {
res.status(500).send({ message: err.message });
});
};
Check out eager loading and how to code associations in the Sequelize docs. Note that I already had associations in the style above in model that didn't work however there are are examples of how to code include statements with the "example1.belongsTo(example2) style and then the include statements inside the 'where' methods in API's.
https://sequelize.org/master/manual/eager-loading.html

Node Sequelize Relational Challenge

I have three tables (all associated model classnames use PascalCase)
schools school_codes course
------ ------ ------
id (pk) code (pk) name
name school_id (fk) school_code (fk)
I'm trying to define sequelize relations, so that this Course lookup returns the associated School:
const courseWithSchool = await models.Course.findOne({
include: [{
model: models.School,
required: true,
}],
})
The mysql for this is very simple.
mysql> select c.*, s.* from courses c inner join school_codes sc on c.school_code = sc.code inner join schools s on s.id = sc.school_id;
How do I define the relations in sequelize models (without modifying existing schema)? Thanks!
Here are the model definitions I have:
schools.js
module.exports = (sequelize, DataTypes) => {
const School = sequelize.define('School', {
name: DataTypes.STRING,
}, { underscored: true, freezeTableName: true, tableName: 'schools' })
return School
}
course.js
module.exports = (sequelize, DataTypes) => {
const Course = sequelize.define('Course', {
id: {
type: DataTypes.STRING,
primaryKey: true,
},
name: DataTypes.STRING,
school_code: {
type: DataTypes.STRING,
references: {
model: 'school_codes',
key: 'code',
}
}
}, { underscored: true, freezeTableName: true, tableName: 'courses' })
return Course
}
schoolcode.js
module.exports = (sequelize, DataTypes) => {
const SchoolCode = sequelize.define('SchoolCode', {
code:{
type: DataTypes.STRING,
primaryKey: true,
references: {
model: 'courses',
key: 'school_code'
}
},
school_id: {
type: DataTypes.INTEGER,
references: {
model: 'schools',
key: 'id',
},
},
}, { underscored: true, freezeTableName: true, tableName: 'school_codes', })
return SchoolCode
}
I'm just looking for the relations to add to the bottom of each model definition - example...
// School.associate = function (models) {
// School.belongsToMany(models.Course, {
// through: 'school_codes',
// foreignKey: 'school_id',
// otherKey: 'code'
// })
// }
We can keep association in its respective model. I prefer to keep association in respective master table rather than mapping table. The idea is to associate source model to target model and its relationship in both direction. For example let us say source model School has one SchoolCode target model and its reverse relation
//school.model.js
module.exports = (sequelize, DataTypes) => {
const School = sequelize.define('school', {
name: DataTypes.STRING,
}, { underscored: true, freezeTableName: true, tableName: 'schools' })
School.associate = function ({SchoolCode, Course}) {
School.hasOne(SchoolCode, {
foreignKey: 'school_id',
})
SchoolCode.belongsTo(School, {foreignKey: 'school_id'})
School.belongsToMany(Course, { through: SchoolCode , foreignKey : 'school_id'}); //added new
}
return School;
}
//course.model.js
module.exports = (sequelize, DataTypes) => {
const Course = sequelize.define('course', {
id: {
type: DataTypes.STRING,
primaryKey: true,
},
name: DataTypes.STRING,
school_code: {
type: DataTypes.STRING,
references: {
model: 'school_codes',
key: 'code',
}
}
}, { underscored: true, freezeTableName: true, tableName: 'courses' })
Course.associate = function ({SchoolCode, School}) {
Course.hasMany(SchoolCode, {
foreignKey: 'code',
})
Course.belongsToMany(School, { through: SchoolCode, foreignKey : 'code'}); //added new
}
return Course;
}
Finally the third model of SchoolCode (Mapping table).
Note that we don't have to add a reference school_code. It is a primaryKey code of same table. We use references mainly to define the foreign keys, no need for reverse definition here.
Hence commented that part from code below.
module.exports = (sequelize, DataTypes) => {
const SchoolCode = sequelize.define('SchoolCode', {
code:{
type: DataTypes.STRING,
primaryKey: true,
// references: {
// model: 'courses',
// key: 'school_code'
// }
},
school_id: {
type: DataTypes.INTEGER,
references: {
model: 'school',
key: 'id',
},
},
}, { underscored: true, freezeTableName: true, tableName: 'school_codes', })
return SchoolCode
}
References : https://sequelize.org/master/manual/assocs.html
You can define relations like
SchoolCode.belongsTo(School, { foreignKey: 'school_id', targetKey: 'id' });
Course.belongsTo(SchoolCode, { foreignKey: 'school_code', targetKey: 'code' });

Sequelize especial methods don't get created when Using sequelize migrations

I am struggling when using sequelize migrations and a many-to-many relationship between Users and Roles.
This is the Users model:
'use strict';
module.exports = (sequelize, DataTypes) => {
const user = sequelize.define('Users', {
username: DataTypes.STRING,
name: DataTypes.STRING,
email: DataTypes.STRING,
password: DataTypes.STRING
}, {});
user.associate = function(models) {
// associations can be defined here
user.belongsToMany(models.Roles, {
through: models.UserRoles
});
};
return user;
};
This is the Roles model:
'use strict';
module.exports = (sequelize, DataTypes) => {
const role = sequelize.define('Roles', {
name: DataTypes.STRING
}, {});
role.associate = function(models) {
// associations can be defined here
role.belongsToMany(models.Users, {
through: models.UserRoles
});
};
return role;
};
This is the "create-user" migration:
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Users', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
username: {
type: Sequelize.STRING,
allowNull: true,
len: [0, 20]
},
name: {
type: Sequelize.STRING,
allowNull: true,
},
email: {
type: Sequelize.STRING,
allowNull: false
},
password: {
type: Sequelize.STRING,
allowNull: false
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Users');
}
};
This is the "create-role" migration:
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Roles', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Roles');
}
};
This is the userRoles Model:
'use strict';
module.exports = (sequelize, DataTypes) => {
const user_role = sequelize.define('UserRoles', {
userId: DataTypes.INTEGER,
roleId: DataTypes.INTEGER
}, {});
user_role.associate = function(models) {
// associations can be defined here
};
return user_role;
};
And last one the "user-roles" migration:
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('UserRoles', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
userId: {
primaryKey: true,
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: 'Users',
key: 'id'
}
},
roleId: {
primaryKey: true,
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: 'Roles',
key: 'id'
}
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('UserRoles');
}
};
The problem happens when I try to access to the user.setRoles() from a controller:
exports.signup = (req, res) => {
console.log('creating new user', req.body.username);
// Save User to Database
User.create({
username: req.body.username,
email: req.body.email,
password: bcrypt.hashSync(req.body.password, 8)
})
.then(user => {
console.log('USER ADDED');
if (req.body.roles) {
Role.findAll({
where: {
name: {
[Op.or]: req.body.roles
}
}
}).then(roles => {
console.log('ROLES ', roles);
user.setRoles(roles).then(() => {
res.send({ message: "User was registered successfully!" });
});
});
} else {
console.log('NO ROLES > Normal User');
// user role = 1
user.setRoles([1]).then(() => {
res.send({ message: "User was registered successfully!" });
});
}
})
.catch(err => {
console.log('ERROR: ', err);
res.status(500).send({ message: err.message });
});
};
When I console.log the user using : console.log(Object.keys(user.__proto__)) I get this array where the special methods haven't been created, any idea what I am doing wrong?
Array(7) ["_customGetters", "_customSetters", "validators", "_hasCustomGetters", "_hasCustomSetters", "rawAttributes", "_isAttribute"]
Many thanks for your help!
Just call all associate functions after registering models. For instance:
const models = path.join(__dirname, 'models')
const db = {}
fs.readdirSync(models)
.filter(function (file) {
return (file.indexOf('.') !== 0) && (file.slice(-3) === '.js')
})
.forEach(function (file) {
var model = sequelize['import'](path.join(models, file))
db[model.name] = model
})
Object.keys(db).forEach(function (modelName) {
if (db[modelName].associate) {
db[modelName].associate(db)
}
})

Sequelize null value in column violates not-null constraint

I'm getting this error when trying to associate a like to a post.
Unhandled rejection SequelizeDatabaseError: null value in column
"userId" violates not-null constraint
Now the following code gets the post id and user id ok, i did a console log. What could i be doing wrong ?
routes/posts.js
router.post('/:userId/like/:postId', (req, res)=> {
models.Post.findOne({
where:{
id: req.params.postId
}
})
.then( (like) => {
if(like){
models.Likes.create({
where:{
userId: req.params.userId,
postId: req.params.postId
},
like:true
}).then( (result) => {
res.status(200).send({
message: 'You have like this post',
like: result
})
})
}
}).catch( (err) => {
res.status(401).send({
message: "Something went wrong",
err: err
})
})
})
here is the likes migration
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Likes', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
like: {
type: Sequelize.BOOLEAN
},
userId: {
allowNull: false,
type: Sequelize.INTEGER,
references: {
model: 'Users',
key: 'id'
}
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Likes');
}
};
Posts migration
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Posts', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
title: {
type: Sequelize.STRING
},
post_content: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
},
userId: {
type: Sequelize.INTEGER,
references: {
model: 'Users',
key: 'id'
}
},
username: {
type: Sequelize.STRING
},
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Posts');
}
};
Like model
'use strict';
module.exports = function(sequelize, DataTypes) {
const Like = sequelize.define('Likes', {
like:{
type:DataTypes.BOOLEAN,
allowNull:true
}
}, {});
Like.associate = function(models) {
Like.belongsTo(models.User, {
onDelete: "CASCADE",
sourceKey: 'userId'
})
Like.belongsTo(models.Post, {
onDelete: "CASCADE",
sourceKey: 'likeId'
})
}
return Like;
}
Post.model
module.exports = (sequelize, DataTypes) => {
const Post = sequelize.define('Post', {
title: DataTypes.STRING,
post_content: DataTypes.STRING,
username: DataTypes.STRING
}, {});
Post.associate = function(models) {
Post.belongsTo(models.User, { foreignKey: 'userId', targetKey: 'id' });
Post.hasMany(models.Likes, { foreignKey: 'postId', sourceKey: 'id' });
};
return Post;
};
extra
add_postId_to_likes
'use strict';
module.exports = {
up: function (queryInterface, Sequelize) {
return queryInterface.addColumn(
'Likes',
'postId',
{
type: Sequelize.INTEGER,
allowNull: true,
references: {
model: 'Posts',
key: 'id',
}
}
)
},
down: function (queryInterface, Sequelize) {
return queryInterface.removeColumn(
'Likes',
'postId'
)
}
};
In your create call in resolver you are not giving it the necessary values, you have a where clause but not actually giving it the value for required userId.. looks like the only value in your model is the Boolean you are setting
I figured it out.
I just used body instead of params for the postId.
router.post('/like', (req, res)=> {
models.Likes.create({
postId: req.body.postId,
userId: req.user.id,
like:true
}).then( (result) => {
res.status(200).send({
message: 'You have like this post',
like: result
});
}).catch( (err) => {
res.status(401).send({
message: "Something went wrong",
err: err
})
})
})
change my like model to this, i was using sourceKey instead of foreign keys
module.exports = function(sequelize, DataTypes) {
const Like = sequelize.define('Likes', {
like:{
type:DataTypes.BOOLEAN,
allowNull:true
},
// userId: {
// type: sequelize.INTEGER,
// references: {
// model: 'Users',
// key: 'id'
// }
// },
}, {});
Like.associate = function(models) {
Like.belongsTo(models.User, {
onDelete: "CASCADE",
foreignKey: 'userId'
})
Like.belongsTo(models.Post, {
onDelete: "CASCADE",
foreignKey: 'likeId'
})
}
return Like;
}
So now i can like a post, and it will attach the postId along with the usersId on the likes table.
like this

Update with association using Sequelize.js

I am trying to update with association using sequelize.js.
I have tried give example on stackoverflow namely the following links:
Sequelize update with association
Sequelize update with association
Updating attributes in associated models using Sequelize
all of these links did not get me to the goal i am trying to accomplish.
My model is as follow, I have a country module and a city module. a country has many cities. please refer to the module bellow.
Please advise.
country.js file
module.exports = function (sequelize, DataTypes) {
var country= sequelize.define('COUNTRY', {
COUNTRY_ID: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
COUNTRY_NAME: DataTypes.STRING,
COUNTRY_CURRENCY: DataTypes.STRING
}, {
freezeTableName: true,
classMethods: {
associate: function (models) {
COUNTRY_ID.hasMany(models.CITIES, {
foreignKey: 'COUNTRY_ID'
})
}
},
instanceMethods: {
updateAssociation: function (onSuccess, onError) {
country.findAll({
where: {
COUNTRY_ID: req.params.country_id
},
include: [
{
model: sequelize.import('./cities.js'),
}
]
})
})
.then(country =>{
const updatePromises = country.map(countries =>{
return countries.updateAttributes(req.body);
});
const updatePromisescities = list.CITY.map(cities =>{
return cities.updateAttributes(req.body.CITYs[0]);
});
return sequelize.Promise.all([updatePromises, updatePromisescities ])
}).then(onSuccess).error(onError);
}
}
});
return country;
};
city.js file
module.exports = function (sequelize, DataTypes) {
var CITY = sequelize.define('LIST_CODE', {
CITY_ID: {
type: DataTypes.INTEGER,
primaryKey: true
},
COUNTRY_ID: {
type: DataTypes.INTEGER,
primaryKey: true
}
}, {
freezeTableName: true,
timestamps: false,
classMethods: {
associate: function (models) {
// associations can be defined here
CITY.belongsTo(models.COUNTRY, {
foreignKey: 'COUNTRY_ID'
})
}
}
});
return CITY;
};

Resources