- Notifications
You must be signed in to change notification settings - Fork4
Middleware utility for your Promises
License
sebelga/promised-hooks
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
Addpre andpost middleware hooks to any function returning a Promise.
Readthe post on Medium for some example.
yarn add promised-hooks# ornpm install promised-hooks --save`
In order to add "pre" and "post" hooks to your promise you need to wrap their containing object (Class/function/object)
consthooks=require('promised-hooks');// ClassclassUser{// some method that returns a PromisesomeMethod(){ ...}}// FunctionfunctionUser(){}User.prototype.someMethod=functionsomeMethod(){ ...}// Objectconstapi={save:function(){ ...}};// Wrap them to add "pre" and "post" hooks functionalities to their methodshooks.wrap(User);hooks.wrap(api);
Adds a middelware to a promise that will be resolved or rejectedbefore the method you are targetting. If the middelware rejects the Promise the original method isnot executed.All the parameters sent to the original methods are available in the arguments of your middleware.
consthooks=require('promised-hooks');classUser{// instance methodssave(){ ...}// works also with static methodsstaticotherMethod(){ ...}}hooks.wrap(User);User.pre('save',doSometingBeforeSaving);functiondoSometingBeforeSaving(){// Access the arguments passed if neededconstargs=Array.prototype.slice.apply(arguments);// the scope (this) is the original Object wrapped// return a PromisereturnnewPromise((resolve,reject)=>{// ... do some async stuff// then resolve or rejectresolve();});}
You can override the original arguments sent to the target method by resolving the middleware with an object containing an "__override" property.
User.pre('save',doSometingBeforeSaving);functiondoSometingBeforeSaving(){returnnewPromise((resolve,reject)=>{// ...resolve({__override:123});// single argument// orresolve({__override:[123,'arg2']});// multi arguments});/** * With the above override, the User.save() method will * receive those arguments instead of the one originally provided */}
Adds a middelware to be executedafter the method you are targetting has beenresolved. If the post middleware fails and rejects the Promise, the original Promise still resolves and the "post" errors are added the response (see below).
If you resolve your post middelware with an object containing an "__override" property (same as with "pre" hook), itwill override the original response.
consthooks=require('promised-hooks');classUser{// instance methodssave(){ ...}// works also with static (prototype) methodsstaticotherMethod(){ ...}}hooks.wrap(User);// Several middleware can be added at once with an ArrayUser.post('save',[postMiddleware1,postMiddleware2]);functionpostMiddleware1(data){// data is the resolved value from the original promised method// or previous "post" hooks with an __override property// do some async stuff ...// and resolve a PromisereturnPromise.resolve();// If needed, you can override the responsereturnPromise.resolve({__override:'my new response'});}functionpostMiddleware2(data){returnnewPromise((resolve,reject)=>{// call async service...myApi.doSomething((err)=>{if(err){/* if the async fails you would then reject your promise. * The original response is *not* overriden * (see errors in "post" hook below) */returnreject(err);}// no errorreturnresolve();});});}
If one of the "post" hook fails and rejects its Promise then aSymbol is added on the response containing an Array with the errors.If the response is a primitive('string', 'number', boolean) then it is firstconverted to an object with a "result" property.
// For example, if the response returned by the targeted method is:'my response'// in case there are "post" hooks error it will be converted to:{result:'my response'}
You access the errors Array with thehooks.ERRORS
Symbol.
consthooks=require('promised-hooks');classUser{save(){ ...}}hooks.wrap(User);User.post('save',postMiddleware);functionpostMiddleware(){returnnewPromise((resolve,reject)=>{// call async service...myApi.doSomething((err)=>{if(err){returnreject(err);}returnresolve();});});}// Somewhere else in your codeconstuser=newUser();user.save().then((response)=>{// The save occurred without issue but// let's check for any "post" hook errorif(response[hooks.ERRORS]){// deal with errors}...});
The scope (this) of the middelware is set by default on the "wrapped" object. If you need to change it at runtime, you can declare a__scopeHook
functionon the object that will be called for each hook. This method, when called, receives 3 parameters:
- the target method
- the arguments (array of arguments sent to the targeted method)
- the hook method name
classUser{save(){}__scopeHook(targetMethod,args,hookMethod){console.log(targetMethod);// "save"console.log(args);// [123]console.log(hookMethod);// "hashPassword"// You can here return any object for the scopeif(hookMethod==='hashPassword'){return{x:'abc'};// set the scope}// If you return "undefined" the scope is not changedreturnundefined;}}hooks.wrap(User);User.pre('save',functionhashPassword(){// Check the scopeconsole.log(this);// { x: 'abc' }});// ...constuser=newUser();user.save(123).then( ...);
consthooks=require('promised-hooks');classUser{save(userData){returnnewPromise((resolve,reject){// ...your logic to save a user thenresolve(response);});}// works also on static methodsstaticotherMethod(){ ...}}// Wrap the class to add hooks functionalitieshooks.wrap(User);// Hash a user password before savingUser.pre('save',(userData)=>{/** * INFO: If you need to access the User class from this middelware * you can not use an arrow function as the scope is lost. * Use a normal function and *this* will be your Class */if(typeofuserData.password!=='undefined'){userData.password=hashString(userData.password);}returnPromise.resolve();// ----------functionhashString(str){// ... logic to hash a stringreturnhashedString;}});// Let's email our newly created userUser.post('save',(response)=>{// response is what the target method returnsconstemail=response.email;// Return a method that returns a PromisereturnyourMailService.sendEmail(email);});// Create new userconstuser=newUser();// Save useruser.save({name:'John',password:'snow'}).then((response)=>{// Save success// Check if there are any errors in our "post" middlewareif(response.[hooks.ERRORS]){// deal with Post hook error}},(err)=>{// Save failed});
I have been inspired by thehooks-fixed library from @vkarpov15 to write this small utility.
About
Middleware utility for your Promises