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:
- Using TypeScript decorators
- 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
.
Feature | Decorator | HookCommand |
---|---|---|
Setup | Requires TypeScript interfaces | Just define and register via context |
Lifecycle | Pre-instantiated (via Application) | Runtime-only |
Flexibility | Tied to class structure | Functional, lightweight |
Use Case | System hooks | Dynamic, 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.