module:events
- Description:
Event queue processor
This module implement simple event publishing and processing logic, useful for logging events for post-processing by backendjs workers or by other systems using shared queues.
All events will have the same structure:
{ subject: "string", // event subject data: { ... }, // event payload, must be an object id: "string", // unique event id, auto-generated: lib.uuid() time: bigint, // auto-generated: lib.clock() origin: "string", // auto-generated: app.origin() sent: "string", // sent queue name as queueName[@subject][#groupName] received: "string", // revceived queue name as queueName[@subject][#groupName] seq: "int", // sequence per queue: 1...N }Features support:
- publishing and processing: SQS, NATS
- publishing only: EventBridge, SNS
If any of
events-worker-queue-XXXparameters are defined then workers subscribe to configured event queues and listen for events.Drivers like NATS support multiple consumers in the same queue using subject/group syntax:
queueName.subjectqueueName#groupNamequeueName.subject#groupName
The
options.groupNameproperty can be used as a group as well when passsed to listen.Multiple event queues can be defined and processed at the same time.
An event processing function takes 2 arguments, an event and callback to call on finish
- Source:
Examples
Create a stream
nats stream add --subjects 'events,events@*' --defaults events
Configured below in bkjs.conf: NATS server for events, routing by prefixes COMPANY-EVENT: or USER-EVENT: and event processor for corresponding events
queue-events = nats://
events-routing = events@user:^EVENT.USER
events-worker-queue = events@user: mymod.syncUserEvents, events@user#log: mymod.logUserEvents
events-routing = events@company:^EVENT.COMPANY
events-worker-queue = events@company: mymod.syncCompanyEvents
The module below logs all user events in the queue and defines an event processor function to sync such events with external service: - /user/... endpoints for managing users - mymod.syncUserEvents is an event processor function which is run by a worker process, it can run on a different host
const { app, api, lib, events } = require("backendjs");
module.exports = {
name: "mymod",
configureMiddleware(options, callback)
{
api.app.post("/user/:op", this.handleUsers);
callback();
}
handleUsers(context) {
... endpoint processing logic, assume context.user contains currently logged in user ...
const event = {
type: context.params[0],
id: context.user.id,
name: context.user.name,
access_time: Date.now()
}
events.putEvent("EVENT.USER." + context.params[0].toUpperCase(), event);
}
syncUserEvents(event, callback)
{
...
}
logUserEvents(event, callback)
{
...
}
}
app.start({ server: true });
Start the server
node mymod.js -jobs-workers 1
Members
(static) args :Array.<ConfigOptions>
- Source:
- Default Value:
[ { "name": "cap-(.+)", "type": "int", "strip": "cap-", "sametype": 1, "descr": "Capability parameters" }, { "name": "worker-queue", "obj": "worker-queue", "type": "map", "merge": 1, "maptype": "list", "onupdate": "", "descr": "Queues to subscribe for workers, same queues can be used at the same time with different functions and channels and consumers, event queue format is `queue.subject#group`", "example": "events-worker-queue = ticket:ticket.processEvents, ticket.inbox#staff: ticket.processInboxEvents, ticket#staff: ticket.processStaffEvents" }, { "name": "worker-options-(.+)", "obj": "workerOptions", "make": "$1", "type": "map", "descr": "Custom parameters by queue name, passed to `queue.listen` on worker start, useful with channels", "example": "-events-worker-options-ticket count:3,raw:1" }, { "name": "worker-delay", "type": "int", "descr": "Delay in milliseconds for a worker before it will start accepting jobs, for cases when other dependencies may take some time to start" }, { "name": "max-runtime", "type": "int", "min": 0, "multiplier": 1000, "descr": "Max number of seconds an event processing can run before being killed" }, { "name": "routing", "obj": "routing", "type": "map", "merge": 1, "maptype": "regexp", "descr": "Routing map by event subject or type", "example": "-events-routing redis:local.+, nats:.+, sqs:billing.+" }, { "name": "routing-options-(.+)", "obj": "routingOptions", "make": "$1", "type": "map", "merge": 1, "descr": "Routing options by queue name, used by `putEvent` to merge with passed queue options", "example": "-events-routing-options-nats groupName:group" }, { "name": "shutdown-timeout", "type": "int", "min": 500, "descr": "Max number of milliseconds to wait for the graceful shutdown sequence to finish, after this timeout the process just exits" } ]
Methods
(async, static) aputEvent(subject, data, optionsopt) → {object}
- Description:
Async version of module:events.putEvent
- Source:
Parameters:
| Name | Type | Attributes | Description |
|---|---|---|---|
subject |
string | event subject, topic, ID, ... |
|
data |
object | an object to be placed as the |
|
options |
object |
<optional> |
queue specific properties |
Returns:
| Type | Description |
|---|---|
| object |
|
Example
const { err, data } = await events.aputEvent("USER-LOGIN", { id: ..., name: ... })
console.log("Sent to:", data.map(x => x.event))
(static) putEvent(subject, data, optionsopt, callbackopt)
- Description:
Place an event into a queue by subject and type
- Source:
Parameters:
| Name | Type | Attributes | Description |
|---|---|---|---|
subject |
string | event subject, topic, ID, ... |
|
data |
object | Array | an object to be placed as the |
|
options |
object |
<optional> |
queue specific properties |
callback |
function() |
<optional> |
(err, data) - where data is a list of objects with event, error status and options sent to each queue: { err, event, options }, it is empty if nothing was sent. |
Example
events.putEvent("USER-LOGIN", { id: ..., name: ... })
events.putEvent("ORDER-SHIPPED", { id: ... })
events.putEvent("social.post.like", { id: ..., liked: ... }, (err, data) => {
if (!err) {
console.log("Sent:", data?.filter(x => !x.err))
console.log("Errors:", data?.filter(x => x.err))
}
})