Defining Hooks
Hooks trigger operations automatically when content changes. When a record is created, updated, deleted, or published, hooks fire the operations you configure.
Hook Interface
The typed interface defines the fields that TypeScript validates at compile time:
interface ApplyConfigHookInput {
key?: string; // Unique identifier for the hook
name?: string; // Display name
description?: string; // What this hook does
event: string; // Lifecycle event to listen for
type?: string; // Target type (defaults to 'operation')
operationKey?: string; // Key of the operation to run
filter?: Record<string, unknown>; // Scope the hook (e.g., { modelKey: 'redirect' })
notificationConfig?: Record<string, unknown>; // Config for notification-type hooks
isActive?: boolean; // Whether the hook is active (default true)
}A hook always runs work by referencing an operation by operationKey — there is no inline url, HTTP method, or headers on a hook. Whether that work runs synchronously or asynchronously is the operation’s concern (its mode), not the hook’s. See Defining Operations.
Lifecycle Events
Record events:
| Event | When It Fires |
|---|---|
RECORD_CREATED | A new record is created |
RECORD_UPDATED | An existing record is updated |
RECORD_DELETED | A record is deleted |
RECORD_PUBLISHED | A record version is published |
RECORD_UNPUBLISHED | A published version is unpublished |
Operation events — fired when an operation execution reaches a terminal state:
| Event | When It Fires |
|---|---|
OPERATION_COMPLETED | Operation finished successfully (payload includes the typed result) |
OPERATION_FAILED | Operation errored (payload includes {code, message, retryable}) |
OPERATION_CANCELLED | Operation cancelled before completing |
OPERATION_TIMED_OUT | Async-callback operation didn’t call back in time |
See Lifecycle Hooks for the full event catalog and payload shapes.
Basic Example
Trigger an operation whenever a record is created:
import { defineConfig } from '@foir/cli/configs';
export default defineConfig({
key: 'my-app',
name: 'My App',
operations: [
{
key: 'notify-team',
name: 'Notify Team',
endpoint: '/notify',
},
],
hooks: [
{
key: 'notify-on-create',
name: 'Notify on new content',
event: 'RECORD_CREATED',
operationKey: 'notify-team',
},
],
});Filtering by Model
Use the filter field to scope a hook to specific models. Without a filter, the hook fires for all models.
hooks: [
{
key: 'sync-on-update',
name: 'Sync products on update',
event: 'RECORD_UPDATED',
operationKey: 'sync-products',
filter: { modelKey: 'product' },
},
]This hook only fires when a product record is updated. Changes to other models are ignored.
Covering Multiple Events
A common pattern is to create hooks for create, update, and delete events on the same model, all pointing to the same operation:
hooks: [
{
key: 'sync-on-create',
name: 'Sync on redirect create',
event: 'RECORD_CREATED',
operationKey: 'redirect-sync',
filter: { modelKey: 'redirect' },
},
{
key: 'sync-on-update',
name: 'Sync on redirect update',
event: 'RECORD_UPDATED',
operationKey: 'redirect-sync',
filter: { modelKey: 'redirect' },
},
{
key: 'sync-on-delete',
name: 'Sync on redirect delete',
event: 'RECORD_DELETED',
operationKey: 'redirect-sync',
filter: { modelKey: 'redirect' },
},
]This ensures the external system stays in sync regardless of how the record changes.
Disabling a Hook
Set isActive to false to keep a hook in your config but stop it from firing:
hooks: [
{
key: 'notify-slack',
name: 'Notify Slack',
event: 'RECORD_PUBLISHED',
operationKey: 'slack-notify',
isActive: false, // Defined but paused
},
]Real-World Example
From the Cloudflare KV Redirect extension — three hooks keep edge redirects in sync with content changes:
import { defineConfig } from '@foir/cli/configs';
export default defineConfig({
key: 'cloudflare-kv',
name: 'Cloudflare KV Redirector',
operations: [
{
key: 'redirect-sync',
name: 'Sync Redirects to Edge',
description: 'Syncs redirect records to Cloudflare KV',
endpoint: '/sync/all',
},
],
hooks: [
{
key: 'sync-on-create',
name: 'Sync on redirect create',
event: 'RECORD_CREATED',
operationKey: 'redirect-sync',
filter: { modelKey: 'redirect' },
},
{
key: 'sync-on-update',
name: 'Sync on redirect update',
event: 'RECORD_UPDATED',
operationKey: 'redirect-sync',
filter: { modelKey: 'redirect' },
},
{
key: 'sync-on-delete',
name: 'Sync on redirect delete',
event: 'RECORD_DELETED',
operationKey: 'redirect-sync',
filter: { modelKey: 'redirect' },
},
],
});Next Steps
- Lifecycle Hooks Concepts — How hooks work under the hood
- Defining Operations — Create the operations that hooks trigger
- Defining Schedules — Trigger operations on a cron schedule instead of events