How to use the Sequelize belongsToMany() method with code examples

The Sequelize belongsToMany() method is used to create a Many-To-Many association between two tables.

Two tables that have a Many-To-Many relationship require a third table that acts as the junction or join table.

Each record in the junction table will keep track of the primary keys of both models.

For example, suppose you have a Many-To-Many relationship between the Student table and the Class table as shown below:

The Enrollments table is the junction table between the Students and Classes tables. It holds two foreign key columns for StudentId and ClassId.

The belongsToMany() method is used to create a Many-To-Many relationship as shown above using Sequelize.

First, you define the models for the tables as shown below:

const Student = sequelize.define(
  "Student",
  { firstName: Sequelize.STRING },
  { timestamps: false }
);

const Class = sequelize.define(
  "Class",
  { className: Sequelize.STRING },
  { timestamps: false }
);

const Enrollment = sequelize.define(
  "Enrollment",
  {},
  { timestamps: false }
);

Notice how the Enrollment model above doesn’t have any attribute defined.

This is because Sequelize will automatically add the StudentId and ClassId attributes when you call the belongsToMany() method.

You need to call the belongsToMany() method and pass the junction table using the through option as shown below:

Student.belongsToMany(Class, { through: Enrollment });
Class.belongsToMany(Student, { through: Enrollment });

The last thing you need to do is to call the sync() method on the three models.

await Student.sync();
await Class.sync();
await Enrollment.sync()

Sequelize will generate and execute the following SQL statements on your database server:

CREATE TABLE IF NOT EXISTS `Students` (
  `id` integer NOT NULL AUTO_INCREMENT,
  `firstName` varchar(255),
  PRIMARY KEY (`id`)) ENGINE = InnoDB;

CREATE TABLE IF NOT EXISTS `Classes` (
  `id` integer NOT NULL AUTO_INCREMENT,
  `className` varchar(255),
  PRIMARY KEY (`id`)) ENGINE = InnoDB;

CREATE TABLE IF NOT EXISTS `Enrollments` (
  `StudentId` integer,
  `ClassId` integer,
  PRIMARY KEY (`StudentId`, `ClassId`),
  FOREIGN KEY (`StudentId`) 
    REFERENCES `Students` (`id`) 
    ON DELETE CASCADE ON UPDATE CASCADE,
  FOREIGN KEY (`ClassId`) 
    REFERENCES `Classes` (`id`) 
    ON DELETE CASCADE ON UPDATE CASCADE) ENGINE = InnoDB;

Now that you have the tables created in your SQL database server, you can perform SQL data manipulation on the tables using Sequelize methods.

INSERT new rows to tables with a Many-To-Many relationship

To INSERT data into tables with a Many-To-Many relationship, you can call the insert() method from one of the non-junction tables (Student or Class)

You need to pass the attributes of the associated table and use the include option in the method call:

const student = await Student.create({
  firstName: "Lucas",
  Classes: [{
    className: "Chemistry",
  }]
}, {
  include: Class
});

In the above create() method, the Student and Class object will be created because both records don’t exist in the tables.

Here are the statements executed by Sequelize:

INSERT INTO `Students` (`id`, `firstName`)
  VALUES (DEFAULT, ?);

INSERT INTO `Classes` (`id`, `className`)
  VALUES (DEFAULT, ?);

INSERT INTO `Enrollments` (`StudentId`, `ClassId`)
  VALUES (?, ?);

Here’s the data saved in the tables:

# Students
+----+-----------+
| id | firstName |
+----+-----------+
|  1 | Lucas     |
+----+-----------+

# Enrollments
+-----------+---------+
| StudentId | ClassId |
+-----------+---------+
|         1 |       1 |
+-----------+---------+

# Classes
+----+-----------+
| id | className |
+----+-----------+
|  1 | Chemistry |
+----+-----------+

But keep in mind that if you already have the record in the Classes table, then calling the create() method again will only create a new row in the Students table.

When you try to INSERT a new student with the same class:

const student = await Student.create({
  firstName: "Jake",
  classes: [{
    className: "Chemistry",
  }]
}, {
  include: Class
});

Only the Students table will save a new row:

# Students
+----+-----------+
| id | firstName |
+----+-----------+
|  1 | Lucas     |
|  2 | Jake      |
+----+-----------+

# Enrollments
+-----------+---------+
| StudentId | ClassId |
+-----------+---------+
|         1 |       1 |
+-----------+---------+

I’m not sure if this is a bug from Sequelize, but this is unacceptable because the Enrollments table should have stored a new record for the new student.

This is why if you want to add a relationship between a new record and an existing record, you need to separate the process as follows:

  • Call the create() method to add the new row to your table
  • Retrieve the row that you want to associate with the new row. Use findByPk() or findOne() method as you see fit.
  • Call the Sequelize add-relation method to insert the relationship row between your tables

Sequelize add-relation method is a method generated for your models when you call the belongsToMany() method.

In case of our example, Sequelize will generate the Students.addClass() and Classes.addStudent() method for the models.

Take a look at the code below:

// 1. INSERT a new student
const student = await Student.create({
  firstName: "Jake",
});

// 2. Find the Classes row
const classRow = await Class.findByPk(1);

// 3. INSERT the association in Enrollments table
await student.addClass(classRow, { through: Enrollment });

Calling the addClass() method above will cause Sequelize to INSERT a new row to the Enrollments table.

Retrieving rows from tables with a Many-To-Many relationship

When you want to retrieve data from the joined tables, you can call the query method from either non-junction tables:

const student = await Student.findOne({
  where: { firstName: "Jake" },
  include: Class
});

console.log(student.toJSON()); 
// ^ Student object instance

The returned data will be as follows:

{
  id: 1,
  firstName: 'Lucas',
  Classes: [ 
    { id: 1, className: 'Chemistry', Enrollment: { StudentId: 1, ClassId: 1 } 
  ]
}

You can reverse the data retrieval from the Class model side as shown below:

const classRow = await Class.findOne({
  where: { className: "Chemistry" },
  include: Student
});

And that’s how the belongsToMany() method works in Sequelize.

This method is more advanced than the other Sequelize association methods, so you need to understand it well before using it.

For more information, you can visit the Sequelize Many-To-Many guide.

I hope this tutorial has helped you learn how to use Sequelize belongsToMany() method. 🙏

Take your skills to the next level ⚡️

I'm sending out an occasional email with the latest tutorials on programming, web development, and statistics. Drop your email in the box below and I'll send new stuff straight into your inbox!

No spam. Unsubscribe anytime.