Node.js Middleware Hooks
raw JSON →`mhook` is a small Node.js library that provides middleware-like hook functionality, particularly useful for structuring relations within entities in applications like ODMs or ORMs. It allows developers to define a set of named "actions" and then bind multiple hook functions to these actions. When an action is "triggered," all associated hooks are executed sequentially. Hooks can be asynchronous, supporting both traditional Node.js error-first callbacks and Promises for indicating completion or failure. The current stable version is 1.0.1. Its key differentiators include a simple API for sequential hook execution and built-in support for both callback and promise-based asynchronous operations, making it flexible for various async patterns. It focuses purely on the hooking mechanism without dictating how the hooked objects should behave beyond providing `on` and `trigger` methods.
Common errors
error My `hook.trigger()` call never finishes, and the callback or promise `then()` block is never executed. ↓
hook.on() for the affected action. Verify that asynchronous hooks either return new Promise(...) or accept done as the last argument and invoke done() when their work is complete. error TypeError: Cannot read properties of undefined (reading 'Hook') or 'Hook is not a constructor' when using `import { Hook } from 'mhook';` ↓
require() syntax: const { Hook } = require('mhook'); for named exports or const mhook = require('mhook'); and then mhook.Hook. error console output shows 'undefined' when passing arguments to hook functions using `hook.trigger('action', [obj])`. ↓
[obj] and use a callback, the signature should be function(obj, done). If you pass [obj1, obj2] and use a promise, it should be function(obj1, obj2). Warnings
gotcha Asynchronous hook functions must explicitly signal completion by either returning a Promise or calling the `done` callback provided as the last argument. Failure to do so will cause the `trigger` operation to hang indefinitely, preventing subsequent hooks or the final callback from executing. ↓
gotcha `mhook` executes hooks sequentially. If a hook performs a long-running synchronous operation, or an asynchronous operation that takes a significant amount of time, it will block the execution of all subsequent hooks and the final `trigger` callback. This can lead to performance bottlenecks and unresponsiveness. ↓
gotcha The `Hook` constructor requires an array of predefined action strings (e.g., `['beforeUpdate', 'afterUpdate']`). Attempting to bind a hook using `hook.on()` or trigger an action using `hook.trigger()` with an action name not explicitly listed in the constructor will result in no operation or an error, as `mhook` does not dynamically register actions. ↓
Install
npm install mhook yarn add mhook pnpm add mhook Imports
- Hook wrong
import { Hook } from 'mhook';correctconst { Hook } = require('mhook'); - Hook wrong
import Hook from 'mhook';correctconst mhook = require('mhook'); const Hook = mhook.Hook; - mhook module object
const mhook = require('mhook');
Quickstart
const { Hook } = require('mhook'); // Correct CJS way to get Hook
// 1. Define actions for the hook instance
const hook = new Hook(['beforeUpdate', 'afterUpdate', 'afterRemove']);
// 2. Add a hook with a callback
hook.on('beforeUpdate', function(done) {
console.log('Hook 1: beforeUpdate (callback style) started.');
// Simulate async work
setTimeout(() => {
console.log('Hook 1: beforeUpdate (callback style) finished.');
done(); // Signal completion
}, 100);
});
// 3. Add another hook with a promise
hook.on('beforeUpdate', function(data) {
console.log('Hook 2: beforeUpdate (promise style) started with data:', data);
return new Promise((resolve) => {
// Simulate async work
setTimeout(() => {
console.log('Hook 2: beforeUpdate (promise style) finished.');
resolve(); // Signal completion
}, 50);
});
});
// 4. Trigger the 'beforeUpdate' action with arguments
console.log('\nTriggering beforeUpdate (callback-based)...');
hook.trigger('beforeUpdate', [{ id: 1, name: 'oldName' }], function(err) {
if (err) {
console.error('Trigger failed (callback):', err);
} else {
console.log('All beforeUpdate hooks done (callback-based).');
}
});
// 5. Trigger the 'beforeUpdate' action again, using the promise return
console.log('\nTriggering beforeUpdate (promise-based)...');
hook.trigger('beforeUpdate', [{ id: 2, name: 'anotherName' }])
.then(() => {
console.log('All beforeUpdate hooks successfully done (promise-based).');
})
.catch((err) => {
console.error('One of the beforeUpdate hooks failed (promise-based):', err);
});