How to write hooks
-
This post is for plugin developers creating hooks to change functionality of NodeBB.
NodeBB supported a hook system since the early days of it's development. This system was built long before async/await was part of nodejs. Because of this the plugin system was built using a callback system.
When core fired a hook it would pass it a callback, and the callback would be called with the result once all the plugins processed the parameters. Here is a sample from the callback system.
// core plugins.hooks.fire('filter:topics.get', { topics: topics }, function (err, result) { console.log(results.topics); //... }); // your plugin myPlugin.filterTopicsGet = function (hookData, callback) { // do something async with topics and call callback somethingAsync(hookData, function (err) { callback(err, hookData); }); }
Once promises and async/await support was widely available we switched core to use async/await. Now the same hook is fired as follows.
// core const result = await plugins.hooks.fire('filter:topics.get', { topics: topics }); console.log(results.topics);
For backwards compatibility plugin hooks written with callback style still work, but you can use async functions in your plugins as well. The above sample would look like this
myPlugin.filterTopicsGet = async function (hookData) { // do something async with topics await somethingAsync(hookData); return hookData; }
Starting with 1.17.0, you can also use synchronous functions if your hook isn't making any async calls. Below are 3 different ways to write hooks, they are all supported.
// async (prefered style for async functions) myPlugin.myHook = async function (hookData) { // do some async call await somethingAsync(hookData); return hookData; }; // callback-style (old style) myPlugin.myHook = function (hookData, callback) { // do some async call somethingAsync(hookData, function (err) { callback(err, hookData); }); } // sync function (new since 1.17.0) myPlugin.myHook = function (hookData) { // set some value on hookData hookData.foo = 1; return hookData; }
You should not mix these styles together. For example do not return a value from the hook and call the callback at the same time.
myPlugin.badHook1 = function (hookData, callback) { hookData.foo = 1; callback(null, hookData); return hookData; }; // or myPlugin.badHook2 = function (hookData, callback) { hookData.foo = 1; return setImmediate(callback, null, hookData); };
Let us know if you have any questions.
-
Hello!
Can you explain the new standards for client-side hooks?
Previously we used the
$(window).on(..., ...)
notation, and now we have the newhooks
module that we can use in our "client" code (was added in 1.17.0).For example
require(['hooks'], function (hooks) { hooks.on('action:ajaxify.end', ({ tpl_url }) => { ... }); });
What the difference between these two ways and what should we use in our plugins?
-
I have a related question please if someone can help with a short example.
I am mainly trying to send some data to my plugin, from the client side to perform an action not available via API.
I thought I could fire a hook on client side and that would get the data to the plugin, but I don't see any data. I am wondering is it possible to fire a hook from the client-side?I can see some hook examples in composer plugin, but I am not sure if I can send data to the server, and have my plugin receive the data and perform an action, is it possible? and any guidance on how I may accomplish it?
I don't need to return a value, I just need some data to do a change server-side but which is not available by API.
Thank you
-
Quick start plugin has an example on how to add a new api route https://github.com/NodeBB/nodebb-plugin-quickstart/blob/master/library.js#L40-L76.
The hooks that are fired client side are for client side code in plugins. If you want to pass data from the client to the server you have two options.
- Create an api route like in quick start plugin
- Create a new socket event listener on the server side and use socket.emit() client side. Example here