Hook Middleware Decorators
Add and remove pre, post, and "around" middleware hooks to class methods on a per class instance basis with the help of class and method decorators.
How To Use
import { Hooks, Hook, HookAsync, hasHooks } from 'hook-middleware-decorators'
@Hooks()
class ClassWithHooks {
@Hook()
hello(person: string) {
const message = `Hello ${person}`;
console.log(message);
return message;
}
@HookAsync()
async helloAsync(person: string) {
const message = `Hello ${person} async`;
console.log(message);
return message;
}
noHook(person: string) {
const message = `No hook called with ${person}`
console.log(message);
return message;
}
}
function preHello(person: string){
console.log('pre', person)
}
function postHello(message: string, person: string){
console.log('post', message, person)
}
function aroundHello(func: (person: string)=>string, person: string): string {
console.log('before hello', person);
const result = func(person);
console.log('after hello', result);
return result;
}
const instance = new ClassWithHooks();
let helloUnsubscriber = () => {}
if(hasHooks<ClassWithHooks>(instance)) { // type-guard to ensure the class is decorated with @Hooks
helloUnsubscriber = instance.around('hello', aroundHello);
instance.pre('hello', preHello);
instance.post('hello', postHello);
instance.pre('helloAsync', preHello);
instance.post('helloAsync', postHello);
instance.pre('noHook', preHello);
instance.post('noHook', postHello);
}
(async () => {
instance.hello('world');
await instance.helloAsync('Earth');
instance.noHook('stuff')
helloUnsubscriber();
instance.hello('Mars');
})();
/**
* Result:
*
* before hello world
* pre world
* Hello world
* post Hello world world
* after hello Hello world
* pre Earth
* Hello Earth async
* post Hello Earth async Earth
* No hook called with stuff
* pre Mars
* Hello Mars
* post Hello Mars Mars
*/
Important
The @Hooks
class decorator adds pre
, post
, and around
methods to the decorated class. Any methods or properties with the same names in the class definition will be overridden.
If a pre
or around
function throws an error, the hooked method will throw the error as well. If a post
function throws an error, the hooked method will not throw the error.
Registering a pre
, post
, or around
function will result in an unsubscriber being returned. Invoking the unsubscriber will remove the function as a middleware.
Use Cases
- Put authorization and validation in
pre
functions - Put metrics in
around
functions where applicable - Put any side effects such as notifications in
post
functions
Best Practices
-
pre
,post
, andaround
functions should refrain from mutating any arguments in order to keep behavior predictable. - Use the
@Hook
decorator for synchronous methods. Make sure middleware functions for synchronous methods are also synchronous. - Use the
@HookAsync
decorator for asynchronous methods.pre
andpost
middleware functions for asynchronous methods can be synchronous or asynchronous.around
middleware functions must be asynchronous