Hooks

Burdy uses hooks to provide extensibility. We have taken the heavy inspiration of WordPress during the development of the functionality of the hook.

You can think of them in terms of a subscriber/observer pattern, or simply s WordPress puts it:

Hooks are a way for one piece of code to interact/modify another piece of code at specific, pre-defined spots. They make up the foundation for how plugins and themes interact with WordPress Core, but they’re also used extensively by Core itself.

They are used on both front-end and back-end to provide extensibility. There are 3 types of hooks available in Burdy:

  • Backend Actions (async) - Used on the back-end. They do not return any values.
  • Backend Filters (async) - Used on the back-end. They return a value (first argument of the callback).
  • Frontend Filters (sync) - Used on the front-end. They return a value (first argument of the callback).

The main difference between an action and a filter can be summed up like this:

  • the action takes the info it receives, does something with it, and returns nothing. In other words: it acts on something and then exits, returning nothing back to the calling hook.
  • a filter takes the info it receives, modifies it somehow, and returns it. In other words: it filters something and passes it back to the hook for further use.

Said another way:

  • an action interrupts the code flow to do something, and then returns back to the normal flow without modifying anything;
  • a filter is used to modify something in a specific way so that the modification is then used by code later on.

The something referred to is the parameter list sent via the hook definition.

Code Usage

Burdy provides an export called Hooks, here is a short description of ways it can be used.

Register the action listener

Action listener is an async callback that is called once a certain action is triggered. As a concrete example, during start-up api/init is called and it provides an instance of the express. Application as the first and only argument. To add our own API we can do the following:

// index.ts

Hooks.addAction('api/init', async (app) => {
  app.get('/hello-world', asyncWrapper(async (req, res) => {
    res.send('Hello World');
  }))
}, { id: 'my-server-init' });

To see your result view http://localhost:4000/hello-world.

Note that id and 3rd argument is optional. It's useful when you want to remove an action. (Hooks.removeAction('server/init', 'my-server-init')).

Trigger an action

Triggering an action allows you to create extensible functions which subscribers (async callbacks) can modify or react to. To trigger an action simply call the doAction with name and parameters.

// index.ts

const createdUser; //Created User
Hooks.doAction('my/createdUser', createdUser, argument2, argument3, ...);

Register the filter listener

Filters are very similar to actions, however, they return a value (mutate, and return the first argument). For example, you can use one of the provided filters to add new models to Burdy.

// index.ts

import MyModel from '../models/my-model'; // TypeORM Entity/Model

Hooks.addFilter('db/models', async (models) => [...models, MyModel]);

This will register your model to Burdy. This means that when migrations are run, this model/entity will be automatically included.

Trigger a filter

To trigger a custom filter, use applyFilter function.

// index.ts

Hooks.addFilter('addNumbers', async (a, b) => a + b);

const result = await Hooks.applyFilters('addNumbers', 5, 4); // Will have value 9 because of added filter above.

Note that this mutates only the first argument, therefore in the next hook, values would be: a = 9 and b = 4 respectively.

Register the front-end filter listener

This is very similar to regular filters, except they are only used on the frontend and are synchronous. As an example, to add a new link to the dashboard we can use:

// admin/index.tsx

Hooks.addSyncFilter('dashboard/sections', () => ({
  key: 'my-section',
  component: //  - // JSX Element
  permissions: ['admin'] // Only admins see this section in dashboard
}))

Trigger the front-end filter

Similarly to back-end filters, you can apply other filters in the following manner:

// admin/index.tsx

Hooks.addSyncFilter('my/filter', 
    (components, someArg) => [...components, someArg.map(
        i => // 
{i}
)] ); const myComponents = Hooks.applySyncFilter('my/filter', [], [1, 2, 3, 4]);

If you render myComponents you will get 4 divs, with children 1, 2, 3, and 4.

TypeScript - Extending Hook Types

To extend the types you can refer to create-burdy-app. Essentially you need to extend a global namespace in the manner as follows:

// global.d.ts

import '@burdyjs/core/src/types/burdy'
export {};

declare global {
  namespace Burdy {
    interface IActions {
      'someNumberHook': [number, string]; // callback type = (a: number, b: string) => string;
      'myModelHook': [MyModel, string, string]; // callback type = (a: MyModel, b: string, c: string) => MyModel
    }
    interface IFilters {
      'addNumbers': [number, number]; // callback type = (a: number, b: number) => void;
    }
    interface ISyncFilters {}
  }

  interface String {
    test: () => void;
  }
}

Copyright © Burdy Technologies. All rights reserved.