Node.js Middleware Hooks

raw JSON →
1.0.1 verified Thu Apr 23 auth: no javascript maintenance

`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.

error My `hook.trigger()` call never finishes, and the callback or promise `then()` block is never executed.
cause An asynchronous hook function bound to the triggered action did not signal its completion (i.e., didn't return a Promise or call its `done` callback).
fix
Inspect all functions added via 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';`
cause This package is CommonJS-only, and ES Module `import` syntax is not supported directly for its exports.
fix
Use CommonJS 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])`.
cause Hook functions declared with `function(done)` or `function()` are not correctly receiving the arguments passed in the `hookArgs` array of `trigger()`. The `done` callback (if used) is always the *last* argument.
fix
Ensure your hook function signature correctly accounts for the arguments you pass. If you pass [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).
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.
fix Ensure all asynchronous `on` handler functions either return a `Promise` that resolves upon completion, or accept a `done` callback as their last argument and invoke `done()` (or `done(error)`) when their operation is finished.
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.
fix Ensure that individual hook functions are efficient. For potentially long-running tasks, consider offloading them to worker threads or processing them asynchronously outside the main hook chain, if their completion isn't strictly necessary before subsequent hooks.
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.
fix Always define all possible action names in the array passed to the `Hook` constructor. Ensure consistency between declared actions and those used in `on()` and `trigger()` calls.
npm install mhook
yarn add mhook
pnpm add mhook

This example demonstrates how to define actions, bind both callback-based and promise-based hook functions to an action, and then trigger that action, showing how arguments are passed and results are handled.

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);
  });