Getting Started
About
feathers-trigger
provides a hook called trigger
. This hook is meant to be used for a subscription pattern. It's inspired by services like IFTTT or Zapier where you automate things by providing a trigger
and an action
. The hook trigger
can be used for methods create
, update
, patch
and remove
. While configuring the trigger
hook you provide a subscription
which checks for context.data
and context.result
and if the check is fulfilled, it runs an action
callback function you provide.
Use cases are notifications or logs like:
'A todo was created/updated/removed'
'User "xy" assigned the todo "get things done" to you'
'The due date of a todo "procrastinate" was delayed by more than 3 days'
Features:
- check for conditions with sift.js
- check for changes like:
{ publishedAt: { $lt: '{{ before.publishedAt }}' } }
subscriptions
can be stored in a database- check for populations or something.
- Written in Typescript
Installation
npm i feathers-trigger
Simple example
Imagine you want to notify users when a post
gets published.
How can this be done? In this example a post
has a publishedAt
property which shows when a post
was published. A post
with publishedAt === null
means that the post
is not published yet. A post
can be created as a draft which is not published.
But how do you know when a post
gets published? Sounds silly, but there are three possibilities:
- A
post
gets created withpublishedAt: { $ne: null }
. - A
post
gets updated by datapublishedAt: { $ne: null }
, it hadpublishedAt: null
before and the result really haspublishedAt: { $ne: null }
. - A
post
gets patched by datapublishedAt: { $ne: null }
, it hadpublishedAt: null
before and the result really haspublishedAt: { $ne: null }
.
How can this be accomplished?
- Check
context.data
forpublishedAt: { $ne: null }
and if that's not true, the subscription is not true. - Check if the post in the database has
publishedAt === null
and therefore is not published yet. You need to check that in abefore
hook. If that's not true, the subscription is not true. - Check if the
context.result
really haspublishedAt: { $ne: null }
(maybe it's handled by another permission hook, or something). If that's not true, the subscription is not true. - If all three checks are true, run the
notify
function.
It's up to you how you define the notify
action. For the example above the solution with feathers-trigger
looks like the following:
// posts.hooks.js
import { trigger } from 'feathers-trigger';
const notifyPublished = trigger({
conditionsData: { publishedAt: { $ne: null } },
conditionsBefore: { publishedAt: null },
conditionsResult: { publishedAt: { $ne: null }},
action: ({ item }, { context }) => {
return context.app.service('/notify').create(item);
}
});
export default {
before: {
create: [ notifyPublished ],
update: [ notifyPublished ],
patch: [ notifyPublished ]
},
after: {
create: [ notifyPublished ],
update: [ notifyPublished ],
patch: [ notifyPublished ]
}
}
// examples
const postsService = app.service('posts');
// scenario 1:
const post = await postsService.create({ title: 'hello', publishedAt: new Date() })
// notification called
await postsService.patch(post.id, { publishedAt: new Date() });
// notification not called again
// scenario 2:
const post = await postsService.create({ title: 'hello', publishedAt: null })
// notification not called yet
await postsService.update(post.id, { title: 'ciao', publishedAt: null });
// notification not called yet
await postsService.update(post.id, { title: 'hello', publishedAt: new Date() });
// notification called
await postsService.update(post.id, { title: 'hello', publishedAt: new Date() });
// notification not called again
// scenario 3:
const post = await postsService.create({ title: 'hello', publishedAt: null })
// notification not called yet
await postsService.patch(post.id, { title: 'ciao' });
// notification not called yet
await postsService.patch(post.id, { publishedAt: new Date() });
// notification called
await postsService.patch(post.id, { publishedAt: new Date() });
// notification not called again
Complex example
Imagine you want to notify a user when his todo
gets delayed. And only when it gets delayed! Todos can be changed by all members of a team. The user only needs to get notified, if another person changes the date of the todo
. If he changes the data himself, we can assume he noticed the change and doesn't need an annoying notification, right?
How can this be done? Each todo
has at least two properties:
userId
: shows who is responsible for thetodo
dueAt
: show when the todo needs to be done at the latest
// todos.hooks.js
import { trigger } from 'feathers-trigger';
const notifyDelay = trigger([{
fetchBefore: true,
conditionsResult: { startsAt: { $gt: "{{ before.startsAt }}" }, userId: { $ne: "{{ user.id }}" } },
action: ({ item }, { context }) => {
return context.app.service('/notify').create(item);
}
}]);
export default {
before: {
create: [ notifyDelay ],
update: [ notifyDelay ],
patch: [ notifyDelay ]
},
after: {
create: [ notifyDelay ],
update: [ notifyDelay ],
patch: [ notifyDelay ]
}
}
Explanation
You may wonder, what happens on conditionsResult
. This is where feathers-trigger
shows its power. It's heavily inspired by mustache.js. If you're not familiar with the concept, please make sure to give it a short glance before continuing here.
The trigger
hook exposes some views-properties for the conditionsData
, conditionsBefore
, conditionsResult
by default (described here). In the example above the property before
(from trigger
before-hook) and user
(from context.params.user
authentication) is used.
The condition for startsAt: { $gt: "{{ before.startsAt }}" }
is only true for the following change:
startsAt
had aDate
value before (e.g. was notnull
)startsAt
now has aDate
valuestartsAt
was earlier as it is now
The condition for userId: { $ne: "{{ user.id }}" }
is only true for the following:
userId
is not the same ascontext.params.user.id
Testing
Simply run npm test
and all your tests in the test/
directory will be run. The project has full support for Visual Studio Code. You can use the debugger to set breakpoints.
Help
For more information on all the things you can do, visit FeathersJS.
License
Licensed under the MIT license.