Skip to main content

Hooks (Filter and Action)

WP-Node supports a hook system inspired by WordPress, including both filters and actions.

Notes: Since WP-Node is written in TypeScript, these hooks are not directly compatible with WordPress core.

Filter​

In WordPress, a filter allows you to modify data at specific points during execution by adding custom callback functions that receive and return the filtered value. You can register a callback using add_filter(), specify the hook name, callback, and optionally, a priority (lower runs first) and number of accepted arguments.

In WP-Node, filters serve the same purpose β€” transforming data β€” but are fully type-safe and suports asynchronous - allowing you to perform async operations (e.g., fetch or validate) during transformation.

Action​

In WordPress, an action allows you to execute logic at specific points without returning a value. You register actions with add_action(), and they’re typically used for side effects (e.g., logging, sending email, updating state).

In WP-Node, actions extend Node.js's EventEmitter - You can also register actions to trigger only once using addOnce() or addCommandOnce().


How Hooks Work​

WP-Node supports two ways to define and register hooks:

  1. Using TypeScript decorators
  2. Using HookCommand utilities

1. Decorators​

Decorators are tightly integrated with the WP-Node Application lifecycle. When you register hook classes with decorators, WP-Node can attach hooks to Context when it's instantiated β€” allowing hooks like core_init to fire immediately after context creation.

2. HookCommand​

HookCommand is a more straightforward, code-based approach. You define commands with type parameters, then register them at runtime through the context.

FeatureDecoratorHookCommand
SetupRequires TypeScript interfacesJust define and register via context
LifecyclePre-instantiated (via Application)Runtime-only
FlexibilityTied to class structureFunctional, lightweight
Use CaseSystem hooksDynamic, ad-hoc hooks

Using Decorators​

Step 1: Declare Interfaces​

You must declare TypeScript interfaces to define the hook names, argument types, and return types.

// Action Hook Interface
declare module "@rnaga/wp-node/types/hooks/actions.d" {
export interface Actions {
custom_action: (n: number) => void;
}
}

// Filter Hook Interface
declare module "@rnaga/wp-node/types/hooks/filters.d" {
export interface Filters {
custom_filter: (n: number) => number;
}
}

Step 2: Create a Hook Class​

Use @hook, @action, and @filter decorators to bind methods to hook names.

@hook("custom_hook") // Optional namespace
export class CustomFilter {
public v = 0;

// This will be called when 'custom_action' is triggered
@action("custom_action")
testAction() {
this.v += 123;
}

// This will modify the input number by adding 10
// Priority 99 means it runs after lower-priority filters
@filter("custom_filter", 99)
testFilter(data: number) {
return data + 10;
}
}

Step 3: Register Hooks​

Register your hook classes with Application before context is created.

Application.registerHooks([CustomFilter]);

Fire hooks

const context = await Application.getContext();

// Fire action
context.hooks.action.do("custom_action", context);

// Apply the filter - the result should be 20
const result = await context.hooks.filter.apply("custom_filter", 10);

Using HookCommand​

Step 1: Create Hook Commands​

Use HooksCommand utility to create typed filter or action commands.

import { HooksCommand } from "@rnaga/wp-node/core/hooks/hooks-command";

// Filter that transforms a number
const filterCommand = HooksCommand.createFilter<number, number>();

// Action that receives a number but returns nothing
const actionCommand = HooksCommand.createAction<number>();

Step 2: Register Hook Commands via Context​

const context = await Application.getContext("single");

// Register a filter command that adds 100 to input
context.hooks.filter.addCommand(filterCommand, (n) => {
return n + 100;
});

// Apply the filter command (result should be 110)
const result = await context.hooks.filter.applyCommand(filterCommand, 10);

// Register an action command that updates a value
let total = 0;
context.hooks.action.addCommand(actionCommand, (m) => {
total += m;
});

// Trigger the action (total becomes 5)
await context.hooks.action.doCommand(actionCommand, 5);

Summary​

  • Use decorators if you want to register hooks statically as part of the Application lifecycle.
  • Use HookCommand for dynamic or one-off hook logic.
  • Both methods offer type-safe and async behavior β€” improving on traditional WordPress hooks.
  • Filters transform and return data.
  • Actions perform side effects and return nothing.