Understanding Sequelize Associations: Part 2: One to Many (1:n) mapping
This is 2nd part of 3 part blog series in which I explain sequelize associations by creating a CRUD app. The description and links of each part is as follows:
- Part 1: One-to-One (1:1) Mapping
- Part 2: One-to-Many (1:n) Mapping
- Part 3: Many-to-Many (n:m) Mapping
This blog assumes that you have basic understanding of NodeJs, Databases and Sequelize.
Source repo for this blog
1. What are we going to do in this blog
In this blog, I will explain 1:n mapping in sequelize by creating a CRUD app. We will be working on the same app we used in the last blog. The tech stack of the app is as follows:
- Backend: HapiJS
- Database: PostgreSQL
- ORM: Sequelize (Obviously!)
In the last blog, we created two tables: users
and userDetails
. In this part, we will create an additional table called posts
. This table will have 1:n relation with our users
table. The foreign key will be userId
which belongs to users
table. The updated ER diagram is as follows:
2. Generate Models
We have already created users
and usersDetails
in the last post. Now, we will create posts
model. Similar to the last time, we will first create our models using sequelize-cli, then modify the generated models according to our associations
2.1 Create models using sequelize-cli
Run the following command to create our new model posts
. This command will also generate relevant migrations.
node_modules/.bin/sequelize model:generate posts --attributes userId:integer,title:string
2.2 Modify models for association
Now that we have our migrations and models generated by sequelize, we will modify them according to our associations. Find the migration titled like: create-posts
and modify the userId
attribute to match as follows:
userId: {
type: Sequelize.INTEGER,
references: { model: 'users', key: 'id' },
onDelete: 'CASCADE',
}
Now, find the model posts
and add associations as follows:
posts.associate = (models) => {
posts.belongsTo(models.users, {
foreignKey: 'userId',
as: 'users'
});
};
This code can be read like: the model posts
has a foreign key userId
, which belongsTo the model users
.
Now that we have modified posts
model. We now need to modify our users
model, in order to associate users
model with posts
. Since, in this case, one user can have multiple posts, we will use hasMany relation on users
model to reflect that. So, next, find the model users
and modify the associations as follows:
users.associate = function (models) {
users.hasOne(models.userDetails, {
foreignKey: 'userId',
as: 'userDetails',
onDelete: 'CASCADE'
}); // we are adding the following now
users.hasMany(models.posts, {
foreignKey: 'userId',
as: 'posts'
});
};
Notice that, users
was previously associated with usersDetails
only. Now users is associated with two models simultaneously i.e. usersDetails
and posts
.
2.3 Side note: what are hasOne, belongsTo, hasMany, belongsToMany ?
I explained this in previous part too, but in order to keep the blog complete, I’m again explaining them
All the four keywords are used to define associations in our sequelize model. One way to differentiate them is to consider the following points:
1. Whenever the foreign key is defined on the source model (the model for which we are writing this association), use belongsTo or belongsToMany
2. If the foreign key is defined on the target model (the model with which we will be associating), use hasOne or hasMany
3. If we are considering multiple mappings (1:n, or n:m), then, we have to use hasMany or belongsToMany
For eg, in this scenario, userDetails
has the foreign key userId
. Hence, I have used hasOne
constraint. Read like: model users
has one key defined on the model userDetails
with name key as userId
.
Similarly, if we had defined a belongsTo relation on userDetails
, this would have been as follows:
userDetails.associate = function (models) {
userDetails.belongsTo(models.users, {
foreignKey: 'userId',
as: 'users'
});
};
This can be read like: model userDetails
has a foreign key which belongs to the model users
. The foreign key is userId
.
2.4 Run migrations
Now that we have all our models and migrations ready. We will run the migrations, so that our corresponding tables are replicated in our database. Run the following command:
node_modules/.bin/sequelize db:migrate
Sequelize will only run new migrations. Hence, migrations of users
and usersDetails
won’t run. We will now be able to see foreign key constraints defined on our posts
table:
3. Creating CRUD app
We will now move on by modifying our CRUD app, we built in the last post. We will add new post
API for working with posts
table. Also, we will have to modify our existing users
API, to reflect new changes. The complete file can be found here: Users API, Posts API
3.1 Create API
Our create API for table posts
will be a POST API
. This API will expect post title and userId. Sequelize will handle the case when the provided userId
doesn’t exist. So essentially, we don’t need to handle that. The code looks like as follows:
Similarly, we need to modify our users API. We now need to pass posts details along with users details. Since, one user can have multiple posts, we need to provide posts details as an array of object. Each object contains details of a post. A sample payload will look as follows:
{
"name": "ABCD",
"userName": "abcd.abcd",
"mobileNum": "1234567890",
"address": "Earth",
"posts": [{
"title": "some post title 1"
}, {
"title": "some post title 2"
}, {
"title": "some post title 3"
}, {
"title": "some post title 4"
}]
}
The corresponding changes in our create users API is as follows:
3.2 Read API
Following is code for reading posts:
We need to modify our users API, so that it now includes relevant records from posts
table too. The changes will be as follows:
3.3 Update API
API for updating posts can be written as shown below:
For update users API, our function now expects an array of posts that needs to be updated. Since, sequelize doesn’t provide any methods to update records with associations. Hence, we are updating all tables one by one. For updating post, we will consider each post separately and update it. The changes look as follows:
In case you want to see the full file, find it here
3.4 Delete API
API for deleting posts can be written as shown follows:
There is no modification required in our delete users API.
Wrapping Up
In this blog, I showed how you can create 1:n Mapping and utilise sequelize functions to do basic tasks. In the next blog, I will write about Many-to-Many (n:m) mapping. Link.
If you have any questions, please comment them. I will try to answer them as soon as possible
Source repo for this blog: link
Complete repo: