Sling Academy
Home/Node.js/Many to Many associations in Sequelize

Many to Many associations in Sequelize

Last updated: December 29, 2023

Introduction

Sequelize is a Node.js ORM for relational databases that allows developers to manage relationships between different database tables with ease. One of the most common and sometimes complicated relationships to manage is the “many-to-many” association, where each record in a table can be associated with multiple records in another table and vice versa. In this tutorial, we’ll explore the concept of many-to-many associations in Sequelize and learn how to implement them step by step, including how to query through these relationships effectively.

Defining Many-to-Many Associations

To define a many-to-many association in Sequelize, we use the belongsToMany method on our models. This method helps create a new joining table, also known as a ‘through’ table, to keep track of the associations between the tables. Here’s a basic example to illustrate this:

const User = sequelize.define('user', { /* ... */ });
const Project = sequelize.define('project', { /* ... */ });

User.belongsToMany(Project, { through: 'UserProjects' });
Project.belongsToMany(User, { through: 'UserProjects' });

In the above example, we defined a User and Project model, and then used the belongsToMany method to establish a many-to-many relationship between them through a joining table named ‘UserProjects’.

Creating and Joining Records

Once the models and their associations are defined, the next step is to create records and join them through the many-to-many relationship. Sequelize offers convenient methods such as create, add, and set to handle these operations. Below is an example of how to create a user with associated projects:

// Create a User with associated Projects
User.create({
    username: 'johndoe',
    Projects: [
        { name: 'Project A' },
        { name: 'Project B' }
    ]
}, {
    include: [Project]
});

Additionally, you might want to join an existing user to an existing project:

User.findByPk(1).then(user => {
    Project.findByPk(1).then(project => {
        user.addProject(project);
    });
});

Advanced Querying

Sequelize’s power lies in its ability to simplify complex queries involving many-to-many relationships. Here’s how you can fetch users with their associated projects, and vice versa:

// Fetch users with their associated projects
User.findAll({ include: Project }).then(users => {
    // Users with their associated projects
});

// Fetch projects with their associated users
Project.findAll({ include: User }).then(projects => {
    // Projects with their associated users
});

This is just scraping the surface of querying many-to-many relationships. Sequelize provides various options to filter these queries, eager load entities, and much more.

Association Scope and Aliasing

As your applications grow more complex, you might find the need to apply scopes to your associations or provide aliases for your models within associations. This can be useful for filtering results or when you have multiple associations between the same models. Consider the following advanced example:

const Role = sequelize.define('role', { /* ... */ });
User.belongsToMany(Role, {
    through: 'UserRoles',
    as: 'roles',
    scope: {
        active: true
    }
});
Role.belongsToMany(User, {
    through: 'UserRoles',
    as: 'users',
    scope: {
        active: true
    }
});

This allows us to create more tailored queries such as finding all users with a specific role:

User.findAll({
    include: [{
        model: Role,
        as: 'roles',
        where: { name: 'Developer' }
    }]
});

Managing the Join Table

Lastly, directly managing the join table may sometimes be necessary, particularly when additional data needs to be stored in the relationship. Sequelize gives you full control over the join table by allowing you to define it as a model:

const UserProjects = sequelize.define('UserProjects', {
    userId: {
        type: Sequelize.INTEGER,
        allowNull: false,
        references: {
            model: 'users',
            key: 'id'
        }
    },
    projectId: {
        type: Sequelize.INTEGER,
        allowNull: false,
        references: {
            model: 'projects',
            key: 'id'
        }
    },
    role: Sequelize.STRING
});

User.belongsToMany(Project, { through: UserProjects });
Project.belongsToMany(User, { through: UserProjects });

This user-defined join table, or ‘through’ model, can hold additional fields and be queried or modified like any other model.

Conclusion

In conclusion, understanding and implementing many-to-many relationships is crucial for handling complex data models in web applications. With Sequelize, developers can simplify these relationships and the operations associated with them with intuitive methods and querying capabilities. The examples provided in this article illustrate the basic and advanced concepts of many-to-many associations in Sequelize, providing a starting point for developers to expand upon them with their own unique business logic and data requirements. Practice is key, so experiment, build, and learn more about Sequelize associations in your own projects.

Next Article: Many to One Associations in Sequelize.js

Previous Article: One to Many associations in Sequelize

Series: Sequelize.js Tutorials

Node.js

You May Also Like

  • NestJS: How to create cursor-based pagination (2 examples)
  • Cursor-Based Pagination in SequelizeJS: Practical Examples
  • MongooseJS: Cursor-Based Pagination Examples
  • Node.js: How to get location from IP address (3 approaches)
  • SequelizeJS: How to reset auto-increment ID after deleting records
  • SequelizeJS: Grouping Results by Multiple Columns
  • NestJS: Using Faker.js to populate database (for testing)
  • NodeJS: Search and download images by keyword from Unsplash API
  • NestJS: Generate N random users using Faker.js
  • Sequelize Upsert: How to insert or update a record in one query
  • NodeJS: Declaring types when using dotenv with TypeScript
  • Using ExpressJS and Multer with TypeScript
  • NodeJS: Link to static assets (JS, CSS) in Pug templates
  • NodeJS: How to use mixins in Pug templates
  • NodeJS: Displaying images and links in Pug templates
  • ExpressJS + Pug: How to use loops to render array data
  • ExpressJS: Using MORGAN to Log HTTP Requests
  • NodeJS: Using express-fileupload to simply upload files
  • ExpressJS: How to render JSON in Pug templates