Jeff Wilhite Jeff Wilhite - 4 months ago 46
Javascript Question

Accessing an associated table from a Sequelize getter method

This is my first node.js web app after 20 years of C++, and I think I just need someone to point me in the right direction here.

I'm writing the server side of an app that deals with schools and students. When the client sends a request (via my REST API) for a particular student, I want to return the student's first and last name and school. I'm using a library called epilogue that parses the requests, reads/writes to the MySQL database via sequelize, and wraps up the result in a JSON response. It will send all the fields I have defined for student as well as the values of any getters I have defined. Everything has been working great. I have been able to use epilogue to POST, PUT, GET and generally keep the MySQL database updated.

However, things got tricky when I tried to define a getter method that returns the student's school name. In order to do that, I have to access a record in the school table and get the school name.



var school = sequelize.define("School", {
SchoolName: {
type: DataTypes.STRING,
},
}, {
classMethods: {
associate: function(models) {
school.hasMany(models.Student);
}
}
}
}

var student = sequelize.define("Student", {
FirstName: {
type: DataTypes.STRING,
},
LastName: {
type: DataTypes.STRING,
},
}, {
classMethods: {
associate: function(models) {
student.belongsTo(models.School);
},
},
getterMethods: {
FullName: function() {
return this.FirstName + ' ' + this.LastName; // This works just fine
},
SchoolName: function() {
return this.getSchool().SchoolName; // This doesn't work because getSchool is asynchronous
}
}
}
}





The problem is that .getSchool() is asynchronous, so it seems I would have to use .then to access the result. Promises are a new concept for me, but I've tried things like:



SchoolName: function {
return this.getSchool.then(function(school) {
return school.SchoolName;
});
}





But it seems this just returns the Promise itself instead of the results of the promise because the promise isn't fulfilled at the time the getter returns. So, I end up sending the client something like:



{
"FullName": "John Doe",
"SchoolName": {
"isFulfilled": false,
"isRejected": false
},
"id":1,
"FirstName":"John",
"LastName":"Doe"
}





It seems like the getters here are necessarily synchronous, so it looks like I just need some way to tell the getter to stop and wait until the Promise is fulfilled and then return the value. But that seems to violate the asynchronous nature of node.js. So, I have a hunch that I am fundamentally misunderstanding something important about sequelize.js or node.js or promises or maybe even database design. Can someone redirect me, please? Mainly, I would like to know: what is the right way to do this?

Answer

Yep, the getSchool() method returns a promise. You need to have the sequelize query include the association. You could do that either manually by making a new webservice and putting in your own sequelize query or by telling epilogue to do that if it can. Look at a unit test in the epilogue code. Maybe you can add the option: associations: true to have associations loaded.