mongoose icon indicating copy to clipboard operation
mongoose copied to clipboard

About asynchronous getters/setters again

Open bigslycat opened this issue 9 years ago • 16 comments

I have read the two discussions (#2571 and #517) that relate to this opportunity, and still do not see any reason to abandon it. Why not? Even if Mongoose should maintain compatibility with the ES5, this does not prevent to check that the setter returns to us. Watch this pseudo-code:

if (setterResult.constructor.name === 'Promise' && setterResult.then) {
  promisesArray.push(setterResult.then(
    value => {
      return {
        path: path,
        value: value
      };
    },
    err => {
      return err;
    }
  ));
}

// ...

if (promisesArray.length){
  return Promise.all(promisesArray).then(
    results => {
      for (let result of results) {
        // Do anything with it
      }
      // Do anything with all fields
      return this.save();
    },
    errors => {
      for (let err of errors) {
        // Do anything with it
      }
      return errors;
    }
  );
} else {
  // Do anything with all fields
  return this.save();
}

So why not? This is a very useful feature, and it will join naturally in existing.

bigslycat avatar Jun 13 '16 11:06 bigslycat

Yeah async setters would be useful. It's more difficult than you think, because the setter would need to resolve before doing validate(), and you'd need to return a promise from .set() because .set() can throw an error if there's a cast error.

vkarpov15 avatar Jun 14 '16 13:06 vkarpov15

Would love to see that coming in Mongoose ! Do you plan to implement that in a next release @vkarpov15 ?

maxs15 avatar Jul 24 '17 14:07 maxs15

@maxs15 possibly. Still haven't investigated how we would implement this feature, so uncertain.

vkarpov15 avatar Jul 28 '17 00:07 vkarpov15

I've written a little plugin to mock async getters/setters until it lands in the core. It uses different properties (read instead of get and write instead of set) to simplify things and middleware (pre init and post save for getters and pre save for setters). It is by no means perfect (I'm especially not happy how it interacts with validators) but it gets the job done.

You can try it here and leave your opinion.

c0d0g3n avatar Sep 07 '17 14:09 c0d0g3n

I like this plugin but there's a few caveats, we'd also have to integrate this queries, because we also support doing things like findByIdAndUpdate(doc, doc)

vkarpov15 avatar Sep 20 '17 00:09 vkarpov15

I would be happy to see that feature.

What's the status?

Ulrikop avatar Mar 08 '18 09:03 Ulrikop

+1

Zwimber avatar Jul 10 '18 15:07 Zwimber

May be we ready for PR? Oh I wait this so long. =)

bigslycat avatar Jul 11 '18 11:07 bigslycat

Hi what is status on this?

Library worked great with mongoose 4.

Matzu89 avatar Jan 30 '19 03:01 Matzu89

@Matzu89 what library?

vkarpov15 avatar Feb 03 '19 15:02 vkarpov15

Any update on this?

lvkins avatar Feb 15 '21 12:02 lvkins

This would be a great feature to add.

gaborszita avatar Sep 12 '21 18:09 gaborszita

Resurrecting this old thread since it is still the latest for this issue, and trying to bump it for visibility.

For my use case, I have a couple of fields that need to be encrypted with libsodium before saving, and that needs to be done asynchronously. As of now I can't use neither get/set, nor a virtual getter/setter and had to settle on using a pre save method which is far from ideal.

sol-villar avatar Oct 21 '24 20:10 sol-villar

pre('save') is our recommended approach right now. Async getters are likely to be tricky to implement in general because JSON.stringify(), res.json() in Express, etc. are synchronous. But async setters could be more doable with the exception of validateSync().

vkarpov15 avatar Nov 12 '24 17:11 vkarpov15

That is what I have done for now @vkarpov15 , created 2 middlewares to intercept the call

ItemSchema.pre(/save|create/, encryptionMethodMiddleware);
ItemSchema.pre(
    /findOneAnd(?:Replace|Update)|bulkWrite|(?:insert|update)Many/,
    encryptionQueryMethodMiddleware
);

It is not as clean as I wanted and it need an extra getter to decrypt, but for now it worked

sol-villar avatar Nov 13 '24 15:11 sol-villar

What we usually do is either 1) put async logic in the business logic rather than a hook or setter, or 2) put encrypted data in a separate model. But registering middleware on all those functions also works well.

vkarpov15 avatar Nov 20 '24 16:11 vkarpov15