trigger
Runs actions for the items of create, update, patch and remove calls that match the conditions of a subscription. The conditions are predicates, tested against the data before the call, the items before the call and the items after it. The items come from defineStash, so subscriptions whose fetches have equal params share them.
Register it as a before and an after hook, or as an around hook.
import { trigger } from 'feathers-utils/hooks';Example
import { trigger } from 'feathers-utils/hooks'
const notifyOnPublish = trigger<PostsContext>({
name: 'notifyOnPublish',
before: (post) => !post.published,
result: (post) => post.published,
action: async ({ result }) => sendMail(result),
})
app.service('posts').hooks({ around: { patch: [notifyOnPublish] } })Type declaration
Show Type Declarations
export type TriggerActionOptions<H extends HookContext, T> = {
context: H
/** All items of the call, matching or not */
items: StashedItem<T>[]
subscription: TriggerSubscription<H, T>
}
export type TriggerAction<H extends HookContext, T> = (
item: StashedItem<T>,
options: TriggerActionOptions<H, T>,
) => Promisable<void>
export type TriggerBatchAction<H extends HookContext, T> = (
items: StashedItem<T>[],
options: TriggerActionOptions<H, T>,
) => Promisable<void>
export type TriggerSubscriptionOptions<
H extends HookContext = HookContext,
T = ResultSingleHookContext<H>,
> = {
/**
* Names the subscription: `params.skipHooks` skips it by this name (see
* `shouldSkip`), and the debug log shows it.
*/
name?: string
/**
* Whether the subscription applies to the call, tested once before it -
* e.g. with `isContext` or `isProvider`.
*
* @example
* ```ts
* trigger({ iff: isContext({ path: 'posts', method: 'patch' }), action })
* ```
*/
iff?: boolean | PredicateFn<H>
/**
* Tests `context.data` before the call - on multi create every item on its
* own, and the action only runs for the items whose data matched. Doesn't
* match without `context.data`, e.g. on `remove`.
*
* On multi create, the data is paired with its result by its id, or without
* one by its position. Data without an id doesn't match if a later hook adds
* or removes an item, since the positions no longer line up.
*/
data?: (data: DataSingleHookContext<H>, context: H) => Promisable<boolean>
/**
* Tests the item before the call. Doesn't match an item without one, e.g.
* on `create`. Fetches the items before the call.
*
* @example
* ```ts
* trigger({ before: (post) => !post.published, action })
* ```
*/
before?: (before: T, context: H) => Promisable<boolean>
/**
* Tests the item after the call.
*
* @example
* ```ts
* trigger({ result: (post) => post.published, action })
* ```
*/
result?: (result: T, context: H) => Promisable<boolean>
/**
* Tests the item before and after the call, to compare them. Fetches the
* items before the call.
*
* @example
* ```ts
* trigger({
* change: ({ before, result }) => before?.status !== result?.status,
* action,
* })
* ```
*/
change?: (item: StashedItem<T>, context: H) => Promisable<boolean>
/**
* Adjusts the params of the fetches before and after the call - e.g. to
* populate the items. See `defineStash`.
*/
transformParams?: StashTransformParams<H>
/**
* Fetches the items before the call. Resolved once per call, before it.
*
* @default `true` with `before` or `change`, `false` otherwise
*/
fetchBefore?: boolean | PredicateFn<H>
/**
* Waits for the actions before the call returns, and fails the call if one
* of them fails. Without, the errors of the actions are logged with
* `console.error`. Resolved once per call, before it.
*
* @default true
*/
isBlocking?: boolean | PredicateFn<H>
/**
* Logs why the subscription skips the call or an item. Resolved once per
* call, before it.
*
* @default false
*/
debug?: boolean | PredicateFn<H>
/**
* Runs for every matching item. Use either `action` or `batchAction`.
*/
action?: TriggerAction<H, T>
/**
* Runs once with all matching items, not at all without any. Use either
* `action` or `batchAction`.
*/
batchAction?: TriggerBatchAction<H, T>
}
export type TriggerSubscription<
H extends HookContext = HookContext,
T = ResultSingleHookContext<H>,
> = TriggerSubscriptionOptions<H, T> &
(
| {
action: TriggerAction<H, T>
batchAction?: never
}
| {
batchAction: TriggerBatchAction<H, T>
action?: never
}
)
export type TriggerOptions<
H extends HookContext = HookContext,
T = ResultSingleHookContext<H>,
> =
| MaybeArray<TriggerSubscription<H, T>>
| ((
context: H,
) => Promisable<MaybeArray<TriggerSubscription<H, T>> | undefined>)
/**
* Runs actions for the items of `create`, `update`, `patch` and `remove`
* calls that match the conditions of a subscription. The conditions are
* predicates, tested against the data before the call, the items before the
* call and the items after it. The items come from {@link defineStash}, so
* subscriptions whose fetches have equal params share them.
*
* Register it as a `before` and an `after` hook, or as an `around` hook.
*
* @example
* ```ts
*
*
* const notifyOnPublish = trigger<PostsContext>({
* name: 'notifyOnPublish',
* before: (post) => !post.published,
* result: (post) => post.published,
* action: async ({ result }) => sendMail(result),
* })
*
* app.service('posts').hooks({ around: { patch: [notifyOnPublish] } })
* ```
*
* @see https://utils.feathersjs.com/hooks/trigger.html
*/
export declare function trigger<
H extends HookContext = HookContext,
T = ResultSingleHookContext<H>,
>(
options: TriggerOptions<H, T>,
): {
(context: H): Promise<void>
(context: H, next: NextFunction): Promise<void>
}| Argument | Type | Description |
|---|---|---|
| options | TriggerOptions<H, T> |
| type | methods | multi |
|---|---|---|
| before, after, around | create, update, patch, remove | yes |
Options
Conditions
All conditions a subscription has must match. They may be async.
- Before the call:
ifftests the call, e.g. withisContextorisProvider, anddatatestscontext.data— on multi create every item on its own. - After the call, for every item:
resulttests the item after the call,beforethe item before it, andchangeboth of them, to compare them.
Items Before and After the Call
The items come from defineStash:
- The items before the call are only fetched when needed: with
before, withchange, or withfetchBefore. - Subscriptions whose fetches have equal params share them, across
triggerhooks as well. transformParamsadjusts the params of the fetches, e.g. to populate the items.
An action gets { id, before, result } for every matching item. batchAction runs once, with all of them.
By default, the call waits for the actions and fails if one of them fails. With isBlocking: false it doesn't wait, and trigger logs the errors of the actions with console.error — nothing else would catch them.
Skipping
A subscription with a name is skipped by params.skipHooks, just like a hook wrapped in skippable:
await app.service('posts').patch(id, data, { skipHooks: ['notifyOnPublish'] })Migrating from feathers-trigger
feathers-trigger | feathers-utils |
|---|---|
service: 'posts', method: 'patch' | iff: isContext({ path: 'posts', method: 'patch' }) |
params: { provider: 'rest' } | iff: isProvider('rest') |
data: { … }, before: { … }, result: { … } | data: (data) => …, before: (before) => …, result: (result) => … |
result: ({ item, before }) => …, a query or a boolean | change: ({ before, result }) => boolean |
manipulateParams | transformParams |
params.skipTrigger: 'name' | params.skipHooks: ['name'] |
action({ before, item }, options) | action({ id, before, result }, options) |
batchAction([[change, options], …], context) | batchAction(items, { context, items, subscription }) |
feathers-trigger matched query objects with sift. To keep a query, pass sift(query) — it returns a predicate.
