# 3.3.1: Sequelize One-To-Many (1-M) Relationships

## Learning Objectives

1. Sequelize provides "special" relationship methods (aka mixins) attached to models to query data with 1-1, 1-M and M-M relationships
2. Understand steps to set up Sequelize's relationship methods
3. Understand how to use Sequelize's relationship methods
4. Understand how to use migrations to add foreign keys needed for Sequelize relationships

## Introduction

Sequelize makes it easy to query for data with "special" relationship methods (aka mixins) built into Sequelize. To set up these methods we will need to tell Sequelize which models are associated with each other and how they are associated, e.g. `users` and `posts` are related via a 1-M relationship. We will also need to ensure our database tables have relevant foreign key columns using migrations if necessary. We will explore what Sequelize relationship methods look like and how to set them up in this submodule.

Recall our `Users` and `Posts` example from the SQL 1-M Relationships submodule where users have many posts, and each post belongs to a single user. In that module we demonstrated how to query posts for a given user using SQL, like the following.

```sql
SELECT * from Posts where userId=2;
```

The equivalent query in Sequelize would look like the following. Note that all Sequelize query methods return JavaScript promises because the methods query a remote database. We can choose to handle these promises with `await` or `.then` syntax.

```javascript
const posts = await Post.findAll({
  where: {
    userId: 2
  },
});
```

We will use Sequelize instead of raw SQL in our apps because writing queries in JavaScript instead of SQL strings allows VS Code to more easily catch syntax errors for us. This makes our apps more robust and less verbose.

## Sequelize Associations

Let's go through Sequelize's official introduction to associations section by section.

### Defining the Sequelize associations

{% embed url="<https://sequelize.org/docs/v6/core-concepts/assocs/>" %}
Official guide to Sequelize Associations
{% endembed %}

1. Notice the 4 types of associations Sequelize uses to specify relationships between models: `HasOne`, `BelongsTo`, `HasMany` and `BelongsToMany`. We will use `BelongsTo` and `HasMany` for 1-M relationships. `HasOne` and `BelongsTo` are used for 1-1 relationships and `BelongsToMany` is used for M-M relationships.
2. To tell Sequelize that model `A` has a 1-M relationship with model `B` and `A` is the "1" and `B` is the "M" in the relationship, we would call `A.hasMany(B);` and `B.belongsTo(A);`. Both calls are needed to include Sequelize relationship methods on both `A` and `B`. Since `B` is the "M" in the relationship, Sequelize will assume there is a foreign key `AId` (by default the related model's name (i.e. `A`) followed by `Id`) that references `A`'s primary key `id` in `B`'s model and underlying SQL table.
3. No need to worry about the options 2nd parameter, `hasOne` and `belongsToMany` methods for now
4. Note in which table Sequelize expects the foreign key to be for each association. When `A.hasMany(B)`, foreign key is in the target model `B`. When `B.belongsTo(A)`, foreign key is in the source model `B`.

### Creating the standard relationships

{% embed url="<https://sequelize.org/docs/v6/core-concepts/assocs/#creating-the-standard-relationships>" %}
Official guide to Sequelize Associations: Creating the standard relationships
{% endembed %}

1. We will focus on One-To-Many relationships in this submodule with `hasMany` and `belongsTo` associations

### One-To-Many relationships (1-M)

{% embed url="<https://sequelize.org/docs/v6/core-concepts/assocs/#one-to-many-relationships>" %}
Official guide to Sequelize Associations: One-To-Many relationships
{% endembed %}

1. Note the explanation that there is only 1 option for which table contains the foreign key in a 1-M relationship: the "M" table.
2. In our apps, we will declare Sequelize associations like `Team.hasMany(Player);` and `Player.belongsTo(Team);` in class definitions in `db/models/team.js` and `db/models/player.js`, the files in which we define our models. We will declare the associations from within each class using the `this` keyword, like `this.hasMany(models.Player)` from the `Team` class and `this.belongsTo(models.Team)` from the `Player` class. More examples in "Update models and migrations" section below.
3. We will not use `sync` as explained in Rocket's previous Sequelize submodule because it can cause unintended behaviour in production.
4. No need to worry about the Options section for now, we will stick to the defaults first

### Basics of queries involving associations

{% embed url="<https://sequelize.org/docs/v6/core-concepts/assocs/#basics-of-queries-involving-associations>" %}
Official guide to Sequelize Associations: Basics of queries involving associations
{% endembed %}

1. Sequelize uses a 1-1 relationship between `Ship` and `Captain` models here but the concepts are the same for 1-M relationships
2. Lazy loading is fetching data without using joins. Eager loading is fetching data with joins using Sequelize syntax.
3. `getShip()` is one of the "special" relationship methods we mentioned at the top of this page that allow us to query related data using Sequelize. In this case, because `Ship` and `Captain` are related with a 1-1 relationship, we can call `getShip()` on an instance of `Captain` to get that captain's ship. More on these methods in the Special methods/mixins section below.
4. Eager loading can be helpful to retrieve associated data in a single database query. We will use this in our Bigfoot SQL M-M exercise.
5. Ignore the `save()` method, we will not use it because we will use the built-in `create`, `update` and `destroy` methods that perform `save` automatically.

### Special methods/mixins added to instances

{% embed url="<https://sequelize.org/docs/v6/core-concepts/assocs/#special-methodsmixins-added-to-instances>" %}
Official guide to Sequelize Associations: Special methods/mixins added to instances
{% endembed %}

1. This section documents all "special" relationship methods we referred to at the top of this page. Note that these relationship methods will only be available on model instances when we associate relevant models with the relevant association methods, e.g. `belongsTo` and `hasMany`. For example, if I declared `A.hasMany(B)` and `B.belongsTo(A)`, I could call `a.getBs()` (where `a` is an instance of model `A`) but not `b.getAs()` (where `b` is an instance of model `B`) because each `B` belongs to only 1 `A`, not multiple.
2. All Sequelize relationship methods return JavaScript promises because they query a remote database
3. In case you're wondering, "foo" and "bar" (or just "foobar") are [common placeholder names in computer science](https://en.wikipedia.org/wiki/Foobar)
4. Note Sequelize performs pluralisation automatically and intelligently such that we can assume the relationship method names use accurate English, e.g. `getPerson` for a 1-1 relationship and `getPeople` for a 1-M relationship with a `Person` model.
5. We can ignore the special methods for `belongsToMany` associations for now. We will revisit them in the Sequelize M-M submodule.
6. Great work! This is the most important section of this submodule, and we will be using these "special" relationship methods often!

### Why associations are defined in pairs?

{% embed url="<https://sequelize.org/docs/v6/core-concepts/assocs/#why-associations-are-defined-in-pairs>" %}
Official guide to Sequelize Associations: Why associations are defined in pairs?
{% endembed %}

1. Always define relationships in pairs in Sequelize, e.g. `hasMany` and `belongsTo` for 1-M relationships! This will ensure we have the relevant built-in relationship methods when we need them.
2. Ignore the sections below this one in the Sequelize Associations page. We will not use them for now, if at all at Rocket. We will solidify our fundamentals first before learning advanced concepts.

## Update models and migrations to add foreign keys

### Introduction

The above documentation showed us how to declare Sequelize associations and use Sequelize relationship methods, but touched little on how to add foreign keys in model definitions and in our database schema using migrations. For Sequelize associations to work we will also need to update model and migration files such that our models and our database have the relevant foreign keys.

Let's again use our hypothetical `Users` and `Posts` social media example where each user has many posts. Because there is a 1-M relationship between `Users` and `Posts` respectively, there needs to be a foreign key `UserId` on the `Posts` table. Let's see how our models and migrations might look with this foreign key.

### Models

Most of `db/models/user.js` where we define our `User` model is boilerplate except for `this.hasMany(models.post)` where we declare `User`'s association with the `Post` model

{% code title="db/models/user.js" %}

```javascript
"use strict";
const { Model } = require("sequelize");
module.exports = (sequelize, DataTypes) => {
  class User extends Model {
    /**
     * Helper method for defining associations.
     * This method is not a part of Sequelize lifecycle.
     * The `models/index` file will call this method automatically.
     */
    static associate(models) {
      this.hasMany(models.post);
    }
  }
  User.init(
    {
      name: DataTypes.STRING,
    },
    {
      sequelize,
      modelName: "user",
      underscored: true,
    }
  );
  return User;
};
```

{% endcode %}

Like `User`, we also declare an association in `Post`, this time `belongsTo` instead of `hasMany`. Unlike `User`, `Post` needs a foreign key to complete their 1-M relationship, and we will give this foreign key a non-default name for clarity. We define this foreign key as `AuthorId` instead of `UserId`, because `UserId` is less precise and could cause confusion as more users are associated with posts, e.g. users that like or comment on posts.

There are 2 points to note:

1. In our association method `belongsTo` we add a 2nd options parameter `{ as: "Author" })` that instructs Sequelize to use the `Author` alias for `User`s in this relationship. This has 2 consequences:
   1. All Sequelize relationship methods that would otherwise have looked like `post.getUser()` will now look like `post.getAuthor()` instead ([official explanation](https://sequelize.org/docs/v6/core-concepts/assocs/#note-method-names))
   2. Sequelize now expects there to be a foreign key `AuthorId` on the `Post` model and `Posts` table instead of `UserId`
2. We define the foreign key `AuthorId` as a property of the `Post` model. In addition to specifying the property's data type, we also specify the model and property this foreign key references, in this case the `id` property of the `User` model.

{% code title="db/models/post.js" %}

```javascript
"use strict";
const { Model } = require("sequelize");
module.exports = (sequelize, DataTypes) => {
  class Post extends Model {
    /**
     * Helper method for defining associations.
     * This method is not a part of Sequelize lifecycle.
     * The `models/index` file will call this method automatically.
     */
    static associate(models) {
      this.belongsTo(models.user, { as: "author" });
    }
  }
  Post.init(
    {
      date: DataTypes.DATE,
      content: DataTypes.TEXT,
      AuthorId: {
        type: DataTypes.INTEGER,
        references: {
          model: "user",
          key: "id",
        },
      },
    },
    {
      sequelize,
      modelName: "post",
      underscored: true,
    }
  );
  return Post;
};
```

{% endcode %}

The above changes will allow us to use Sequelize relationships in our apps, assuming our underlying SQL database also has the relevant foreign keys. We will need to add new database migrations to add relevant foreign keys to our database schema.

### Migrations

The following database migrations assume we do not have existing tables in our database. If we do have existing tables in our database whose schemas need to be edited, we will either need to:

1. Edit existing migrations, drop existing database (`dropdb` is a convenient CLI method), create new database (`createdb` is a convenient CLI method) and re-run migrations (`npx sequelize db:migrate`)
2. Create new migrations to edit tables in existing database (`npx sequelize migration:generate`)

Rocket recommends option #1 for apps with no live user data to maximise development speed. Apps with live user data only have option #2, since we should never drop a database with live user data.

The migration to create the `users` table has no foreign key, since there is no foreign key on the `User` model.

{% code title="20220531155824-create-user.js" %}

```javascript
"use strict";
module.exports = {
  async up(queryInterface, Sequelize) {
    await queryInterface.createTable("users", {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: Sequelize.INTEGER,
      },
      name: {
        type: Sequelize.STRING,
      },
      created_at: {
        allowNull: false,
        type: Sequelize.DATE,
      },
      updated_at: {
        allowNull: false,
        type: Sequelize.DATE,
      },
    });
  },
  async down(queryInterface, Sequelize) {
    await queryInterface.dropTable("users");
  },
};
```

{% endcode %}

The migration to create the `posts` table contains the `author_id` foreign key, declared almost identically as it was in `db/models/post.js` above. Sequelize expects the value for the `models` key in the `references` object to reference a plural table name. Documentation for declaring foreign keys in migrations [here](https://sequelize.org/api/v6/class/src/dialects/abstract/query-interface.js~queryinterface#instance-method-createTable).

{% code title="20220531155825-create-post.js" %}

```javascript
"use strict";
module.exports = {
  async up(queryInterface, Sequelize) {
    await queryInterface.createTable("posts", {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: Sequelize.INTEGER,
      },
      date: {
        type: Sequelize.DATE,
      },
      content: {
        type: Sequelize.TEXT,
      },
      author_id: {
        type: Sequelize.INTEGER,
        references: {
          model: "users",
          key: "id",
        },
      },
      created_at: {
        allowNull: false,
        type: Sequelize.DATE,
      },
      updated_at: {
        allowNull: false,
        type: Sequelize.DATE,
      },
    });
  },
  async down(queryInterface, Sequelize) {
    await queryInterface.dropTable("posts");
  },
};
```

{% endcode %}

Once we have updated our models to include associations and foreign keys, updated our migrations to include foreign keys and run those migrations on our database, we are ready to start using Sequelize relationship methods in our apps!

{% hint style="info" %}
**New to Rocket Academy?**

If you're not enrolled in Rocket's Bootcamp and visiting this page, [check out our website](https://www.rocketacademy.co/courses/bootcamp-course) to learn more about our Bootcamp course!
{% endhint %}
