Node.js & Sequelize: How to Make Join Queries

Updated: December 29, 2023 By: Guest Contributor Post a comment

Introduction

When working with databases in a Node.js application, managing relationships between tables becomes essential for complex queries. Sequelize, a promise-based Node.js ORM (Object-Relational Mapping) library, provides a robust set of tools to handle such tasks. In this tutorial, we will explore how to perform join queries using Sequelize to fetch related data efficiently from multiple tables in a SQL database.

Before we dive into the code examples, it is critical to understand the types of joins that Sequelize supports:

  • Inner Join: Returns records that have matching values in both tables.
  • Left Outer Join: Returns all records from the left table, and the matched records from the right table.
  • Right Outer Join: Returns all records from the right table, and the matched records from the left table.
  • Full Outer Join: Returns all records when there is a match in either left or right table.

We will start with simple join queries and gradually move to more complex scenarios including associations, aliasing, and advanced filtering through the examples.

Setting up Sequelize

Before we begin, make sure you have Sequelize and the relevant database driver installed in your Node.js project:

npm install sequelize
npm install pg pg-hstore // For PostgreSQL

Next, configure Sequelize to connect to your database:

const Sequelize = require('sequelize');
const sequelize = new Sequelize('database', 'username', 'password', {
  host: 'host',
  dialect: 'postgres' // or any other dialect
});

Basic Join Queries

Let us consider two models: User and Profile where a user has one profile. Our goal is to join these tables using Sequelize’s findOne and findAll methods.

Defining the models:

const User = sequelize.define('User', {
  username: Sequelize.STRING
});
const Profile = sequelize.define('Profile', {
  birthday: Sequelize.DATE
});

// Defining the relationship
User.hasOne(Profile);
Profile.belongsTo(User);

Performing an Inner Join:

User.findOne({
  include: [{
    model: Profile
  }] 
}).then(userWithProfile => {
  console.log(userWithProfile);
});

So, in the example above, Sequelize automatically performs an inner join between Users and Profiles based on their defined relationship.

Advanced Join Queries

As we progress, let’s illustrate a more advanced scenario where you have multiple associations and you’d like to use aliasing for clarity.

Suppose a User can have multiple Posts and a Post can have many Comments. We want to retrieve a single post along with its author’s profile and all associated comments.

Defining the additional models:

const Post = sequelize.define('Post', {
  content: Sequelize.TEXT
});
const Comment = sequelize.define('Comment', {
  text: Sequelize.STRING
});

User.hasMany(Post);
Post.belongsTo(User);
Post.hasMany(Comment);
Comment.belongsTo(Post);

Now, let’s craft our join query with aliasing:

Post.findOne({
  where: { id: 1 },
  include: [
    {
      model: User,
      include: [
        {
          model: Profile,
          as: 'userProfile' // Aliased
        }
      ]
    },
    {
      model: Comment,
      as: 'postComments' // Aliased
    }
  ]
}).then(post => {
  console.log(post);
});

This code example demonstrates using aliases to make queries more readable and organized, especially when dealing with multiple nested joins.

Filtering Join Queries

A common requirement is to apply conditions to your join queries. Sequelize offers various ways to add filters through the where option.

For example, suppose we want to fetch all users who have a profile with a birthday this month:

User.findAll({
  include: [{
    model: Profile,
    where: {
      birthday: {
        [Sequelize.Op.gte]: new Date(new Date().getFullYear(), new Date().getMonth(), 1),
        [Sequelize.Op.lt]: new Date(new Date().getFullYear(), new Date().getMonth() + 1, 0)
      }
    }
  }]
}).then(users => {
  console.log(users);
});

The example uses Sequelize’s operators to define a range for the birthday field. Note that Sequelize filters the join condition but still returns all User records that match the criteria, along with their associated Profiles.

Using Raw SQL for Complex Joins

While Sequelize can handle many complex scenarios, sometimes you may need to write raw SQL queries for maximum flexibility. Sequelize provides methods such as sequelize.query for executing custom SQL queries.

An example of a raw SQL join query:

sequelize.query(`
  SELECT *
  FROM Users AS U
  INNER JOIN Profiles AS P ON U.id = P.UserId
  WHERE P.birthday > :date
`, {
  replacements: { date: new Date(2000, 0, 1) },
  type: Sequelize.QueryTypes.SELECT
}).then(results => {
  console.log(results);
});

The above snippet showcases how a raw SQL query might look like when executed through Sequelize, providing an option for cases where ORM methods do not suffice.

Conclusion

In this tutorial, we covered the basics of performing join queries in Node.js using Sequelize. Starting from simple inner joins to more complex scenarios involving filtering and raw SQL, Sequelize proves to be a powerful tool in the arsenal of a Node.js developer. With the knowledge of these techniques, you can efficiently query and manage relational data, ultimately creating robust, data-driven applications.

Remember that while convenience is a great aspect of using an ORM like Sequelize, understanding the underlying SQL is crucial for optimizing your queries and troubleshooting any issues that may arise. Happy coding!