'beforeBulkDestroy not finding model property to change

I am trying to use the beforeBulkDestory Sequelize hook on a user delete that will switch the deleted column boolean to true prior to updating the record to add a timestamp for deleted_at. However, when I console.log the function parameter it provides a list of options and not the model object that I can update for the record of focus. Am I approaching this the wrong way? Is this something that should be set using model instances?

API Call:

import db from '../../../models/index';
const User = db.users;

export default (req, res) => {

    const {
        query: { id },
    } = req

    console.log(User)
  
    if (req.method === 'DELETE') {
        User.destroy({
            where: {
                id: id
            }
        }).then(data => {
            res.json({
            message: 'Account successfully deleted!'
            })
        })
    } else {
        const GET = User.findOne({
            where: {
                id: id
            }
        });
        GET.then(data => {
        res.json(data)
        })
    }
  
}

Parameter Values (beforeBulkDestroy, afterBulkDestroy):

beforeBulkDestroy
{
  where: { id: '5bff3820-3910-44f0-9ec1-e68263c0f61f' },
  hooks: true,
  individualHooks: false,
  force: false,
  cascade: false,
  restartIdentity: false,
  type: 'BULKDELETE',
  model: users
}
afterDestroy
{
  where: { id: '5bff3820-3910-44f0-9ec1-e68263c0f61f' },
  hooks: true,
  individualHooks: true,
  force: false,
  cascade: false,
  restartIdentity: false,
  type: 'BULKUPDATE',
  model: users
}

Model (users.js):

'use strict';
const Sequelize = require('sequelize');
const { Model } = require('sequelize');
const bcrypt = require("bcrypt");

module.exports = (sequelize, DataTypes) => {
  class users 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) {
      // define association here
    }
  };

  users.init({
    id: {
      type: DataTypes.UUID,
      defaultValue: Sequelize.UUIDV4,
      primaryKey: true
    },
    first_name: DataTypes.STRING,
    last_name: DataTypes.STRING,
    password: {
      type: DataTypes.STRING
    },
    email: DataTypes.STRING,
    active: {
      type: DataTypes.BOOLEAN,
      defaultValue: true
    },
    deleted: {
      type: DataTypes.BOOLEAN,
      defaultValue: false
    }
  }, {
    hooks: {
      beforeDestroy: (user, options) => {
        console.log("beforeDestroy")
        console.log(user)
        console.log(options)
        user.deleted = true
      }
    },
    sequelize,
    freezeTableName: true,
    modelName: 'users',
    omitNull: true,
    paranoid: true,
    underscored: true,
    createdAt: 'created_at',
    updatedAt: 'updated_at',
    deletedAt: 'deleted_at',
    hooks: {
      beforeCreate: async function(user){
        console.log("beforeCreate")
        console.log(user)
        const salt = await bcrypt.genSalt(12);
        user.password = await bcrypt.hash(user.password, salt);
        console.log(user.password)
      },
      beforeBulkDestroy: async function(user){
        console.log("beforeBulkDestroy")
        console.log(user)
      },
      afterBulkDestroy: async function(user){
        console.log("afterDestroy")
        console.log(user)
      }
    }
  });

  users.prototype.validPassword = async function(password) {
    console.log("validatePassword")
    console.log(password)
    return await bcrypt.compare(password, this.password);
  }

  return users;
};


Solution 1:[1]

the before/after bulkDestroy hooks only receive the options, not the instances. One way you could do this is defining a before/after Destroy hook:

hooks: {
  beforeDestroy: (user, { transaction }) => {
    user.update({ deleted: true }, { transaction });
  }
}

and calling User.destroy with the individualHooks option:

User.destroy({ where: { id: id }, individualHooks: true });

Be aware that this will load all selected models into memory.

Docs

Note: In your case, since you're only deleting one record by id, it would be better to just user = User.findByPk(id) then user.destroy(). This would always invoke the hooks and it also makes sure the record you want to delete actually exists.

Note 2: Not sure why you need a deleted column, you could just use deletedAt and coerce it into a boolean (with a virtual field if you want to get fancy).

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 Felipe Zavan