Let me start with a little disclaimer. Sequelize is one of my least favorite ORMs: it’s inconsistent, it’s unintuitive, it’s not suitable for complex DB operations, queries it generates are not very efficient, and above all it comes with lacking documentation. I know it’s open source and all and I should know better than to complain, but I honestly wish that someone had warned me beforehand — using plain SQL with mysql2 driver would have saved us a lot of effort. If I were to start the project now, I would probably go with objection.js or js-data.
One of the first challenges you come across when dealing with sequelize is model loading. The way proposed by Sequelize documentation is convoluted, not OOP and is not very testable, we therefore had to devise our own loader.
When it comes to data and config structures, we prefer to isolate them in a way that makes them readable from anywhere in the code. A model definition is really a simple object outlining attributes/properties and relationships. So, let’s start there: we are going to build a simple app with two models
Upload , where users can own uploaded files and have one of the uploaded files as their avatar. Let’s define our models as simple objects.
Now let’s create a
BaseModel class that will extend Sequelize model and allow us to load model definitions, establish relationships between models, and register the necessary hooks. While you have probably read that OOP and class extensions result in less readable code, I believe in this case using interfaces and abstract classes provides convient way to abstract your model logic. Given that Sequelize doesn’t do piping nor have async getters, using a base class allows us to write our own logic around models without having to hack Sequelize.
Here is the barebones
BaseModel . Please take a look at my previous article to see what
We can now extend this
BaseModel with our custom classes, or do nothing, if there is no additional logic. In case of the
User, we would want to make sure the password is bcrypted, whenever a model is saved, so 1) we need to define the class to be used for
User instances in our model/schema definition, 2) create the user model class.
As you can see
BaseModel::registerHooks is an abstract method, which we can override in our user model class:
Now we just need to load all the models into our DB service, like so:
So, now we can instantiate our database service, using our IoC (or without it), and get access to our models. When we bootstrap our app, we just need to load the models once: