Search code examples
javascriptnode.jstypescriptmongodbmongoose

Mongoose model middleware post is throwing type error


I would like to delete all 'Shifts' related to 'Event', this code throws this error in VSCode:

No overload matches this call. The last overload gave the following error. Argument of type '"remove"' is not assignable to parameter of type 'RegExp | "insertMany"'.

And this on next() function:

This expression is not callable.

Type 'NativeError' has no call signatures.

const eventSchema = new Schema<EventInterface>({
  userId: { 
    type: String,
    required: true 
  },
  studio: {
    type: Schema.Types.ObjectId,
    ref: 'Studio'
  },
  shifts: [{ 
    type: Schema.Types.ObjectId,
    ref: 'Shift'
  }],
  date: {
    type: Date,
    required: true
  },
  class: {
    type: Schema.Types.ObjectId,
    ref: 'Class',
    required: true
  },
  isActive: {
    type: Boolean,
    default: true
  }
}, {
  timestamps: true,
  versionKey: false
});

// Middleware to handle post-remove event
eventSchema.post<EventInterface>('remove', { document: true }, async function (next) {
  const event = this;
  const Shift = model('Shift');

  try {
    // Delete all related Shift documents
    await Shift.deleteMany({ _id: { $in: event.shifts } });
  } catch (error) {
    next(error);
  }
});


Solution

  • From this PR:

    because for 7.0 the .remove() function was just renamed .deleteOne()

    And this issue:

    remove() was deprecated in a previous version, and removed in Mongoose 7. So this is expected.

    They should update the documentation.

    The solution is to use the deleteOne document middleware.

    E.g.

    import mongoose from 'mongoose';
    import util from 'util';
    import { config } from '../../config';
    
    mongoose.set('debug', true);
    console.log(mongoose.version);
    
    interface EventInterface {
        userId: string;
        shifts: any[];
    }
    const shiftSchema = new mongoose.Schema();
    const Shift = mongoose.model('Shift', shiftSchema);
    
    const eventSchema = new mongoose.Schema<EventInterface>({
        userId: String,
        shifts: [
            {
                type: mongoose.Schema.Types.ObjectId,
                ref: 'Shift',
            },
        ],
    });
    
    eventSchema.post<EventInterface>('deleteOne', { document: true, query: false }, async function (doc, next) {
        const event = this;
        const Shift = mongoose.model('Shift');
        console.log('doc:', doc, doc instanceof Event);
        console.log('this:', this, this === doc);
        try {
            await Shift.deleteMany({ _id: { $in: event.shifts } });
        } catch (error) {
            next(error);
        }
    });
    const Event = mongoose.model('Event', eventSchema);
    
    (async function main() {
        try {
            await mongoose.connect(config.MONGODB_URI);
            // seed
            const [s1, s2] = await Shift.create([{}, {}]);
            const [e] = await Event.create([{ userId: '1', shifts: [s1, s2] }]);
    
            await e?.deleteOne();
        } catch (error) {
            console.error(error);
        } finally {
            await mongoose.connection.close();
        }
    })();
    

    Logs:

    7.3.2
    Mongoose: shifts.insertOne({ _id: ObjectId("64b0d22b22fff59262045d23"), __v: 0 }, {})
    Mongoose: shifts.insertOne({ _id: ObjectId("64b0d22b22fff59262045d24"), __v: 0 }, {})
    Mongoose: events.insertOne({ userId: '1', shifts: [ ObjectId("64b0d22b22fff59262045d23"), ObjectId("64b0d22b22fff59262045d24") ], _id: ObjectId("64b0d22b22fff59262045d27"), __v: 0}, {})
    Mongoose: events.deleteOne({ _id: ObjectId("64b0d22b22fff59262045d27") }, { session: null })
    doc: {
      userId: '1',
      shifts: [
        { _id: new ObjectId("64b0d22b22fff59262045d23"), __v: 0 },
        { _id: new ObjectId("64b0d22b22fff59262045d24"), __v: 0 }
      ],
      _id: new ObjectId("64b0d22b22fff59262045d27"),
      __v: 0
    } true
    this: {
      userId: '1',
      shifts: [
        { _id: new ObjectId("64b0d22b22fff59262045d23"), __v: 0 },
        { _id: new ObjectId("64b0d22b22fff59262045d24"), __v: 0 }
      ],
      _id: new ObjectId("64b0d22b22fff59262045d27"),
      __v: 0
    } true
    Mongoose: shifts.deleteMany({ _id: { '$in': [ ObjectId("64b0d22b22fff59262045d23"), ObjectId("64b0d22b22fff59262045d24") ] }}, {})
    

    As you can see, when you call the event.deleteOne() method, it will trigger the post hook of the deleteOne middleware.