iAmNem
iAmNem

Reputation: 11

Associations using Sequelize: Many to Many relationships

I am trying to build a web chat application that uses express, sequelize and mysql. My logic is that a user belongs to many chats and that a chat belongs to many users. I have created a junction table for this association called User_chat. I have only recently learned about sequelize and am still coming to grips with the logic. I have a simple get request:

router.get('/', withAuth, async (req, res) => {
  try {
    const userChatRooms = await Chat.findAll({
      include: [{ model: User_chat }]
    });

    console.log(userChatRooms);
    res.json(userChatRooms);
  } catch (e) {
    console.log(e);
    res.status(500).json(e);
  }

});

which always returns the following error:

EagerLoadingError [SequelizeEagerLoadingError]: User_chat is not associated to Chat!
    at Function._getIncludedAssociation (/Users/nem/Desktop/proj/chat/node_modules/sequelize/lib/model.js:710:13)
    at Function._validateIncludedElement (/Users/nem/Desktop/proj/chat/node_modules/sequelize/lib/model.js:614:53)
    at /Users/nem/Desktop/proj/chat/node_modules/sequelize/lib/model.js:509:37
    at Array.map (<anonymous>)
    at Function._validateIncludedElements (/Users/nem/Desktop/proj/chat/node_modules/sequelize/lib/model.js:504:39)
    at Function.findAll (/Users/nem/Desktop/proj/chat/node_modules/sequelize/lib/model.js:1723:12)
    at async /Users/nem/Desktop/proj/chat/controllers/api/chat-routes.js:8:27

I would like to be able to return the chat_name of the user logged in. The query i had in mind is:

select c.email, a.chat_name from chat a
left join user_chat b on a.chat_id = b.chat_id
left join user c on b.user_id = c.user_id
where c.user_id = 1;

My association table is shown below:

const { Chat } = require('./Chat');
const { User } = require('./User');
const { User_chat } = require('./User_chat');
const { Message } = require('./Message');

// Defining associations between the models

//TODO: Define between user and chat - many to many
//Junction table is user_chat
Chat.belongsToMany(User, { through: 'User_chat' });

//TODO: Define between chat and user - many to many
//Junction table is user_chat
User.belongsToMany(Chat, { through: 'User_chat' });

//TODO: Define between user and message - one to many
User.hasMany(Message, {
  foreignKey: 'sender_id',
  onDelete: 'CASCADE'
});

//TODO: Define between chat and message - one to many
Chat.hasMany(Message, {
  foreignKey: 'chat_id',
  onDelete: 'CASCADE'
});

//TODO: Define between message and chat - one to one
Message.belongsTo(Chat, {
  foreignKey: 'chat_id'
});

//TODO: Define between message and user - one to one
Message.belongsTo(User, {
  foreignKey: 'sender_id'
});

module.exports = {
  Chat,
  User,
  User_chat,
  Message
};

Model Design


const { Model, DataTypes } = require('sequelize');
const bcrypt = require('bcryptjs');
const sequelize = require('../config/connection.js');

class User extends Model {
  checkPassword(userLoginPassword) {
    return bcrypt.compareSync(userLoginPassword, this.password);
  }
}

User.init(
  //TODO: Define the model for users
  {
    user_id: {
      type: DataTypes.INTEGER,
      autoIncrement: true,
      primaryKey: true
    },
    username: {
      type: DataTypes.STRING(30),
      allowNull: false,
      unique: true
    },
    email: {
      type: DataTypes.STRING(255),
      allowNull: false,
      unique: true
    },
    password: {
      type: DataTypes.STRING(255),
      allowNull: false
    }
  },
  {
    hooks: {
      async beforeCreate(newUserData) {
        newUserData.password = await bcrypt.hash(newUserData.password, 10);
        return newUserData;
      }
      // async beforeUpdate(updatedUserData) {
      //   updatedUserData.password = await bcrypt.hash(
      //     updatedUserData.password,
      //     10
      //   );
      //   return updatedUserData;
      // }
    },
    sequelize,
    timestamps: false,
    freezeTableName: true,
    underscored: true
  }
);

module.exports = { User };
const { Model, DataTypes } = require('sequelize');

const sequelize = require('../config/connection.js');

class Chat extends Model {}

Chat.init(
  //TODO: Define the model for chats
  {
    chat_id: {
      type: DataTypes.INTEGER,
      autoIncrement: true,
      primaryKey: true
    },
    chat_name: {
      type: DataTypes.STRING(255),
      unique: true,
      allowNull: false
    }
  },
  {
    sequelize,
    timestamps: false,
    freezeTableName: true,
    underscored: true
  }
);

module.exports = { Chat };
const { Model, DataTypes } = require('sequelize');

const sequelize = require('../config/connection.js');

class User_chat extends Model {}

User_chat.init(
  //Junction table for the user and chat id since it will be many to many.
  {
    user_id: {
      type: DataTypes.INTEGER
      references: {
        model: 'User',
        key: 'user_id'
      }
    },
    chat_id: {
      type: DataTypes.INTEGER
      references: {
      model: 'Chat',
      key: 'chat_id'
      }
    },
    is_owner: {
      type: DataTypes.BOOLEAN
    }
  },
  {
    sequelize,
    timestamps: false,
    freezeTableName: true,
    underscored: true
  }
);

module.exports = { User_chat };
const { Model, DataTypes, literal } = require('sequelize');

const sequelize = require('../config/connection.js');

class Message extends Model {}

Message.init(
  //TODO: Define the model for message
  {
    message_id: {
      type: DataTypes.INTEGER,
      primaryKey: true,
      autoIncrement: true
    },
    message_content: {
      type: DataTypes.STRING(255),
      allowNull: false
    },
    chat_id: {
      type: DataTypes.INTEGER,
      references: {
        model: 'chat',
        key: 'chat_id'
      }
    },
    sender_id: {
      type: DataTypes.INTEGER,
      references: {
        model: 'user',
        key: 'user_id'
      }
    }
    // created_at: {
    //   type: 'TIMESTAMP',
    //   defaultValue: literal('CURRENT_TIMESTAMP'),
    //   allowNull: false
    // }
  },
  {
    sequelize,
    timestamps: false,
    freezeTableName: true,
    underscored: true
  }
);

module.exports = { Message };

Upvotes: 0

Views: 1343

Answers (1)

Anatoly
Anatoly

Reputation: 22813

If you wish to get all user's chats then indicate User model in include and not the junction model. Moreover it's easier to start a query with a user because you wish to add a condition for it:

const userWithChats = await User.findAll({
      where: {
        user_id: 1
      },
      attributes: ['email'],
      include: [{
        model: Chat,
        attributes: ['chat_name']
      }]
    });
const chats = userWithChats.Chats

Upvotes: 1

Related Questions