dchester / epilogue

Create flexible REST endpoints and controllers from Sequelize models in your Express app
846 stars 116 forks source link

Create an association after insert (Transactions) #146

Closed jansenignacio closed 8 years ago

jansenignacio commented 8 years ago

I've recently started using Epilogue, and it's been a really useful tool to get my API set-up quickly and efficiently. However, the lack of documentation hinders my progress. I've tried looking at the source code and the test cases, but I couldn't find an answer to this question. I think there should be a clean way to do this, but I can't figure it out yet.

Problem To simplify the problem suppose I have the following models: Student, Class, Student_Class

Class.belongsToMany(Student, {through: "Student_Class", as: "class", foreignKey: "class_id"})
Student.belongsToMany(Class, {through: "Student_Class", as: "student", foreignKey: "student_id"})

A Class can have many Students, and a Student can belong to many Classes. My models and fields are all different from this, but this is the simplest real world example I could think of. Anyway, I have the database and a login system set-up. What I want to do is this:

Suppose I am a Student and I'm currently logged in to the system. For some reason, I have rights to create a new Class. I create a new Class. I want to automatically be a Student of that class.

From a technical perspective, this means that when the endpoint to create a new Class is called, there should be a hook of some sort that would create an entry in the Student_Class table using the ID of the currently logged in user. Supposedly, the way to do this would be a series of before and after hooks, but I'm unsure of how to set it up. I also want to be able to control when the commit will be called. I want to have the ability to gracefully control the commits and rollbacks so that I won't have problems further down the road. Basically, how do I handle Transactions? Or should I just roll out my own endpoint and use Sequelize's transactions for complicated matters?

mbroadst commented 8 years ago

@jansen24 you're correct in thinking you would want to use milestones to achieve these goals.

If you take a look at the source for the CreateController, that is the code run every time you would attempt to create a new e.g. Class. So let's take your first example of assigning a user to a newly created class:

classResource.create.write.before((req, res, context) => {
   if ( ! /* check headers, attributes, etc from req */ ) {
     res.send(403, <some error about permissions>);
     return context.skip();   // skips the rest of processing
   }

   // okay you are authorized
  return context.continue();
});
let StudentClass = require('path/to/your/model`);
classResource.create.write.after((req, res, context) => {
   // context.instance === the newly created `Class` instance from sequelize
   return StudentClass.insert(....)
     .then(() => context.continue());
});

Information about the currently logged in user can be obtained at any of these steps, but is sort of out of scope for an epilogue discussion. You might, for instance, write your own middleware for whatever web framework (restify/express) you're using to locally store the id of the Student instance when the person actually logs in, in which case the after milestone above would be as simple as inserting req.currentStudentId or something similar.

Hopefully after that example, you can see how working with transactions would be quite similar: you simply need put milestones around the relevant controllers action (write for Update/Create in this case) and handle the transactions there. Each of the resources has a model property associated with it, so you can use that reference in place during the milestone definition.

Epilogue is meant simply to provide some baseline default behaviors, but is otherwise incredibly flexible.

To your note about documentation: this project is (and has for some time) been maintained and extended primarily by a single developer. I haven't actually had the need to use the module for a couple of years now, and therefore it simply cannot be on the top of my list of priorities unfortunately. We pretty often have complaints about a desire for better documentation, however after receiving an answer the interest in that documentation seems to fade. I hope that we find a way for you to move forward using epilogue in your project, and that when you've figured it out you'll consider spending a little bit of time in exchange to add to the documentation effort. I'm certain others in the future will appreciate your effort, as you may have if others had thought to do so before you.

jansenignacio commented 8 years ago

Thanks for the quick and very informative reply @mbroadst! I'll have a go at implementing the milestones using your example as a guide. I'll let you know if I run into any more trouble.

I'm not making any promises but if I find that Epilogue will be a big part of my project, I'll see to it that I contribute in some way to the documentation. If I don't have the time to write a tutorial or a documentation, maybe I can whip up a simple API that uses the functionalities of Epilogue that I've used so far.