'Sequelize M:N association not creating through table record

When I create a recipe with associated tags using the through option, no record is created in the joining table in the mysql database I'm connected to.

Here are my models definitions:

export const Recipe = sequelize.define('Recipe', {
    // Model attributes are defined here
    title: {
        type: DataTypes.STRING,
        allowNull: false
    },
    image: {
        type: DataTypes.STRING,
        allowNull: true
    },
    prepTime: {
        type: DataTypes.DOUBLE,
        allowNull: false
    },
    cookTime: {
        type: DataTypes.DOUBLE,
        allowNull: false
    },
    totalTime: {
        type: DataTypes.DOUBLE,
        allowNull: false
    },
    servings: {
        type: DataTypes.INTEGER,
        allowNull: false
    },
    rating: {
        type: DataTypes.INTEGER,
        allowNull: false
    },
    notes: {
        type: DataTypes.STRING, allowNull: true
    },
}, {
    // Other model options go here
    tableName: 'Recipes'
});

export const Tag = sequelize.define('Tag', {
    // Model attributes are defined here
    name: {
        type: DataTypes.STRING,
        allowNull: false
    },
}, {
    // Other model options go here
    tableName: 'Tags'
});

export const RecipeTag = sequelize.define('RecipeTag', {
    // Model attributes are defined here
}, {
    // Other model options go here
    timestamps: false,
    tableName: 'RecipeTags'
});

Here are my associations:

Recipe.belongsToMany(Tag, {
    through: RecipeTag,
    foreignKey: 'recipeId',
    as: 'tags'
})

Tag.belongsToMany(Recipe, {
    through: RecipeTag,
    foreignKey: 'tagId',
    as: 'recipes'
})

Here is the create call:

Recipe.create(args, {
                model: Tag,
                through: RecipeTag,
                as: 'tags'
            });

And here is the data:

{
  "title": "Test Recipe",
  "image": "test",
  "prepTime": 20,
  "cookTime": 40,
  "totalTime": 60,
  "servings": 2,
  "rating": 5,
  "categoryId": 1,
  "tags": [
    {
      "name": "New tag",
      "id": 1
    }
  ],
}

With this set up the create method only creates a new recipe. How can I use the create method to add a record to the joining RecipeTags table at the same time as creating a new recipe? I've managed to get it working by doing something like this:

args.tags.map(async (tag: { tagId: number }) => {
    await RecipeTag.create({tagId: tag.tagId, recipeId: recipe.id})
});

But I'd rather have it done in the create if it's possible.



Solution 1:[1]

You need to wrap the association options with include.

Recipe.create(args, {
    include: {
        model: Tag,
        through: RecipeTag,
        as: 'tags'
    }
});

UPDATE:

In order to prevent the duplicates, you can add ignoreDuplicates option and data must include the primary key value.

{
  "title": "Test Recipe",
  ...
  "tags": [
    {
      "name": "New tag",
      "id": 1   # this is important
    }
  ]
}

Then

Recipe.create(args, {
    include: {
        model: Tag,
        through: RecipeTag,
        as: 'tags',
        ignoreDuplicates: true  // Add this
    }
});

There were some bugs for this option, I suggest you to use the newer version of Sequelize, if you haven't updated lately.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1