Pulling associated data fields in controller API's with Sequelize / Node - node.js

I have built a REST api to pull user log data in my Node backend. What is the best course of action to get the fields data of the associated tables? Would I have to manually add to my API the code to pull the data from other tables or is there a easier way of doing this with sequelize? Does this mean the way I have coded my foreign keys are incorrect?
The ID fields below are the ones I need to text data relating to the Int values as they won't mean anything to a user viewing a table on the front-end.
Note: I have the fields data in my Redux store on the front-end so I could also do something there If that is the most common process. I thought that the "include: {all: true}" part would pull the foreign key data but it hasn't worked.
"data": [
{
"diveID": 46,
"diveTypeID": 1,
"diveSchoolNameID": 4,
"diveCurrentID": 1,
"diveVisibilityID": 1,
"diveDate": "2020-01-09T00:00:00.000Z",
"diveMaxDepth": 15,
"diverUserNumber": 6,
"diveVerifiedBySchool": false,
"diveNotes": "Cold, poor view, miserable, seen catfish, eels",
"diveSpotID": null,
"divePhotos": null
},
{
"diveID": 47,
"diveTypeID": 1,
"diveSchoolNameID": 4,
"diveCurrentID": 1,
"diveVisibilityID": 1,
"diveDate": "2020-01-09T00:00:00.000Z",
"diveMaxDepth": 15,
"diverUserNumber": 6,
"diveVerifiedBySchool": false,
"diveNotes": "Cold, poor view, miserable, seen catfish, eels",
"diveSpotID": null,
"divePhotos": null
},
diveLog
module.exports = (sequelize, Sequelize) => {
const diveLog = sequelize.define("diveLogs", {
diveID: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false
},
diveTypeID: {
type: Sequelize.INTEGER
},
diveSchoolNameID: {
type: Sequelize.INTEGER
},
diveCurrentID: {
type: Sequelize.INTEGER
},
diveVisibilityID: {
type: Sequelize.INTEGER
},
diveDate: {
type: Sequelize.DATE
},
diveMaxDepth: {
type: Sequelize.INTEGER
},
diverUserNumber: {
type: Sequelize.INTEGER
},
diveVerifiedBySchool: {
type: Sequelize.BOOLEAN
},
diveNotes: {
type: Sequelize.TEXT
},
diveSpotID: {
type: Sequelize.INTEGER
},
divePhotos: {
data: Sequelize.Buffer,
type: Sequelize.TEXT
}},
{
timestamps: false
}, {});
diveLog.associate = function(models){
diveLog.belongsTo(models.diveRegion, {foreignKey: 'diveRegionID', as: 'diveRegion'})
diveLog.belongsTo(models.certification, {foreignKey: 'certificationID', as: 'certification'})
diveLog.belongsTo(models.current, {foreignKey: 'currentID', as: 'current'})
diveLog.belongsTo(models.visibility, {foreignKey: 'visibilityID', as: 'visibility'})
diveLog.belongsTo(models.divePoint, {foreignKey: 'diveSpotID', as: 'diveSpot'})
};
return diveLog;
};
controller method
exports.userDiveLog = (req, res) => {
try {
const id = req.params.id;
diveLog.findAll({
include: {all: true},
where: {diverUserNumber: id}
})
.then(diveLog => {
const userDiveLogList = [];
for (i = 0; i < diveLog.length; i++) {
userDiveLogList.push(diveLog[i].dataValues);
}
if (!userDiveLogList) {
return res.status(404).send({message: "No dive logs belonging to this user"});
}
res.status(200).send({
data: userDiveLogList
})
})
} catch(err) {
res.status(500).send({
message: "Error retrieving dive log belonging to user id= " + id
});
}
};

Related

NodeJs Join two tables using sequalize and retrieve the data in Controller

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'});

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

findAll sequelize with model returning odd values

Solved thanks to : https://stackoverflow.com/a/64702949/14585149
I am looking to list all transactions I have on a mysql database with the associated users.
I have 2 tables / models : Transaction & User
Transaction :
module.exports = function(sequelize, DataTypes) {
return sequelize.define('transactions', {
transaction_id: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true
},
user_id: {
type: DataTypes.INTEGER(11),
allowNull: false,
references: {
model: 'users',
key: 'user_id'
}
},
account_id: {
type: DataTypes.INTEGER(11),
allowNull: false,
references: {
model: 'accounts',
key: 'account_id'
}
},
}, { tableName: 'transactions' }); };
User :
module.exports = function(sequelize, DataTypes) {
return sequelize.define('users', {
user_id: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
references: {
model: 'recipients',
key: 'created_by'
}
},
contact_id: {
type: DataTypes.INTEGER(11),
allowNull: true,
references: {
model: 'contacts',
key: 'contact_id'
}
}, }, {
tableName: 'users',
timestamps: false
});
};
I have made the associations :
Transaction.hasOne(User, {foreignKey:'user_id'});
User.belongsTo(Transaction, {foreignKey:'user_id'});
and my code is :
api.get('/trx', async (req, res) => {
Transaction.findAll({
attributes: ['transaction_id','user_id','account_id'],
include: [{
model: User,
attributes: ['user_id']}]
})
.then(intrx => res.json(intrx))
.catch(res.catch_error)
});
the result :
[
{
"transaction_id": 1,
"user_id": 4,
"account_id": 1,
"user": {
"user_id": 1
}
},
{
"transaction_id": 2,
"user_id": 4,
"account_id": 75,
"user": {
"user_id": 2
}
}
]
why the values of user_id are different ?
I am expecting the user_id = 4 instead of 1 in my first result and user_id = 4 in my second result.
what I am doing wrong ?
You should reverse your associations like this:
Transaction.belongsTo(User, {foreignKey:'user_id'});
User.hasOne(Transaction, {foreignKey:'user_id'});
because Transaction has link to User i.e. belongs to it.

Sequelize : Only returning one of many result for a nested include

I have 3 tables:
Job post
recruitment phase
Interview slot
Their association is,
jobpost has many recruitment phases and
recruitment phase has many interview slots
I am able to get all the recruitment phases of a job post by including the recruitmentphase association and group clause.
const jobPosts = await JobPost.unscoped().findAll({
where,
include: [
{
model: db.RecruitmentPhase,
include: [{
model: db.InterviewSlot,
},
],
group: ['RecruitmentPhases.id'],
});
But I am only getting one interview slot for the recruitment phase, event though there are many interviewslots for that recruitment phase.
I tried to do group clause inside include.
const jobPosts = await JobPost.unscoped().findAll({
where,
include: [
{
model: db.RecruitmentPhase,
group: ['InterviewSlots.id'],
include: [{
model: db.InterviewSlot,
},
],
group: ['RecruitmentPhases.id'],
});
but it also giving only one interview slot
EDIT
jobpost model :
module.exports = (sequelize, DataTypes) => {
const jobPost = sequelize.define('JobPost', {
id: {
type: DataTypes.BIGINT,
allowNull: true,
autoIncrement: true,
primaryKey: true,
},
jobTitle: {
type: DataTypes.STRING(150),
allowNull: true,
},
}, {
timestamps: true,
defaultScope: {
attributes: { exclude: ['createdAt', 'updatedAt'] },
},
});
jobPost.associate = (models) => {
jobPost.hasMany(models.RecruitmentPhase);
};
return jobPost;
};
Recruitment phase model :
module.exports = (sequelize, DataTypes) => {
const recruitmentPhase = sequelize.define('RecruitmentPhase', {
id: {
type: DataTypes.BIGINT,
allowNull: true,
autoIncrement: true,
primaryKey: true,
},
phaseName: {
type: DataTypes.STRING(200),
allowNull: true,
},
}, {
timestamps: true,
});
recruitmentPhase.associate = (models) => {
recruitmentPhase.belongsTo(models.JobPost);
recruitmentPhase.hasMany(models.InterviewSlot);
};
return recruitmentPhase;
};
Interview slot model :
module.exports = (sequelize, DataTypes) => {
const interviewSlot = sequelize.define('InterviewSlot', {
id: {
type: DataTypes.BIGINT,
allowNull: true,
autoIncrement: true,
primaryKey: true,
},
interviewDate: {
type: DataTypes.DATE,
allowNull: true,
},
});
interviewSlot.associate = (models) => {
interviewSlot.belongsTo(models.RecruitmentPhase);
};
return interviewSlot;
};
Remove group: ['RecruitmentPhases.id'], in order to see the details of InterviewSlots. As is, you're seeing a summary of interview slots...
So, I am not quite sure about, this is the correct answer and is the correct way to do it.
But When I do the grouping for that nested table 'interviewslot' with this '->' , It worked.
group: ['RecruitmentPhases.id', 'RecruitmentPhases->InterviewSlots.id'],
Why do you seem to be fetching JobPost instead of RecruitmentPhase if that is what you are looking to fetch from the database?
From what I understand - you are looking to fetch all the RecruitmentPhases with their job post accompanying the InterviewSlots allocated for each RecruitmentPhase.
Possible Code:
(Updated)
const rec = await db.RecruitmentPhase.findAll({
include:[{model: db.JobPost, where:{ id:job_id }}, {model: db.InterviewSlot}]
});
res.json(rec)
//Expected JSON Data
{
"RecruitmentPhase":[
{
"id":1,
"phaseName":"Phase 1",
"JobPosts": {
"id":1,
"jobTitle":"XYZ"
},
"InterviewSlots":[
{//inteview slot #1 data},
{//interview slot #2 data}
]
},
{
"id":2,
"phaseName":"Phase 2",
"JobPosts": {
"id":1,
"jobTitle":"XYZ"
},
"InterviewSlots":[
{//inteview slot #1 data},
{//interview slot #2 data}
]
}
]
}

Sequelize set associated value

I have two models, Employees and Offices. Every Employee belongs to one Office, and an Office has many Employees.
I am having difficulty figuring out how to update an Employee's office using Sequelize.
The Employee model is as follows:
let Employee = sequelize.define("Employee", {
id: {
field: 'id',
type: DataTypes.INTEGER,
primaryKey: true,
allowNull: false,
autoIncrement: false
},
name: {
field: 'name',
type: DataTypes.STRING(255),
allowNull: false
}
}, {
freezeTableName: true,
timestamps: false,
deletedAt: false
})
Employee.associate = models => {
Employee.belongsTo(models.Office, {
foreignKey: 'id'
})
}
The Office model is as follows:
let Office = sequelize.define("Office", {
id: {
field: 'id',
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: {
field: 'name',
type: DataTypes.STRING(255),
allowNull: false
}
}, {
freezeTableName: true,
tableName: 'Lkup_Office',
timestamps: false,
deletedAt: false
})
Office.associate = models => {
Office.hasMany( models.Employee, {
foreignKey: 'id'
})
}
In the database I have the following Employee:
{
"id": "2",
"name" : "John",
"office": {
"id": 2,
"name" : "Seattle"
}
}
... and the following Offices:
[
{
"id": 1,
"name" : "Chicago"
},
{
"id": 2,
"name" : "Seattle"
}
]
I want to change the ID of Employee(1)'s office from 2 ('Seattle') to 1 ('Chicago'); the problem is that with the following query
// PUT /2/office/1
router.put('/:employeeId/office/:officeId', (req, res) => {
models.Employee.findOne({
where:{id:req.params.employeeId},
include:[{model:models.Office}]
}).then( employee => {
models.Office.findOne({
where:{id:req.params.officeId},
include:[{model:models.Employee}]
}).then( office => {
employee.setOffice( office ).then( result => {
res.send( result )
})
})
})
})
... my Employee's office is not updated:
{
"id": "2",
"name" : "John"
"office": {
"id": 2
"name" : "Seattle"
}
}
It doesn't, in fact, do anything at all: no errors, the DB isn't changed. I have a suspicion that there's something I'm not doing correctly, but Sequelize isn't throwing any errors.
router.put('/:employeeId/office/:officeId', (req, res) => {
/*In this first part, I just separated the two queries into two
separate variables*/
let employeeInfo = models.Employee.findOne({
where: {
id: req.params.employeeId
},
include: [{
model: models.Office
}]
});
let officeInfo = models.Office.findOne({
where: {
id: req.params.officeId
},
include: [{
model: models.Employee
}]
});
/*In this next part, we have to use Promise.all, which comes with
bluebird, which is already a part of Sequelize.js. How this next
part is executed will depend on how you have your Sequelize.js code
structured in your application. This may require some trial and error*/
models.sequelize.Promise.all([employeeInfo, officeInfo]).then(([Employee, Office])=> {
return Employee.setOffice(Office);
}).catch(err => {
console.log(err);
});
});

Resources