https://github.com/nikku/camunda-worker-node
Implement your external task workers for Camunda in NodeJS.
https://github.com/nikku/camunda-worker-node
camunda external-task workers
Last synced: 6 months ago
JSON representation
Implement your external task workers for Camunda in NodeJS.
- Host: GitHub
- URL: https://github.com/nikku/camunda-worker-node
- Owner: nikku
- License: mit
- Created: 2015-08-26T16:47:18.000Z (about 10 years ago)
- Default Branch: master
- Last Pushed: 2021-09-15T12:30:50.000Z (about 4 years ago)
- Last Synced: 2025-04-15T14:56:57.094Z (6 months ago)
- Topics: camunda, external-task, workers
- Language: JavaScript
- Homepage: https://www.npmjs.com/package/camunda-worker-node
- Size: 299 KB
- Stars: 51
- Watchers: 3
- Forks: 27
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# camunda-worker-node
[](https://github.com/nikku/camunda-worker-node/actions/workflows/CI.yml)
Implement your [external task workers](https://docs.camunda.org/manual/latest/user-guide/process-engine/external-tasks/) for [Camunda](http://camunda.org) in [NodeJS](https://nodejs.org/).
> Compatible with Camunda `>= 7.8`. Requires NodeJS `>= 8.6`.
## Usage
This library exposes a simple API to implement external task workers for [Camunda](http://camunda.org).
```javascript
var Worker = require('camunda-worker-node');
var Backoff = require('camunda-worker-node/lib/backoff');var engineEndpoint = 'http://localhost:8080/engine-rest';
var worker = Worker(engineEndpoint, {
workerId: 'some-worker-id',
use: [
Backoff
]
});// a work subscription may access and modify process variables
worker.subscribe('work:A', [ 'numberVar' ], async function(context) {var newNumber = context.variables.numberVar + 1;
// fail with an error if things go awry
if (ooops) {
throw new Error('no work done');
}// complete with update variables
return {
variables: {
numberVar: newNumber
}
};
});// stop the worker instance with the application
worker.stop();
```Make sure you properly configured the [external tasks](https://docs.camunda.org/manual/latest/user-guide/process-engine/external-tasks/) in your BPMN 2.0 diagram:
```xml
```
## Features
* Subscribe to work [node-style](#work-node-style) or via [`async` functions](#work-with-async-function)
* Complete tasks with updated variables or fail with errors
* Trigger [BPMN errors](#trigger-bpmn-errors)
* [Configure and extend task locks](#task-locks)
* Configure [logging](#logging) and [authentication](#authentication)
* [Configure task fetching](#task-fetching)
* [Control the worker life-cycle](#worker-life-cycle)
* [Extend via plug-ins](#extend-via-plug-ins)
* [Customize Variable Serialization](#variable-serialization)## Resources
* [Example](./example)
* [Issues](https://github.com/nikku/camunda-worker-node/issues)
* [Changelog](./CHANGELOG.md)## Implementing Worker
Implement your workers via `async`, promise returning functions or pass results via node-style callbacks.
### Work, Node Style
Use the provided callback to pass task execution errors and data, node-style:
```javascript
// report work results via a node-style callback
workes.subscribe('work:B', function(context, callback) {var newNumber = context.variables.numberVar + 1;
// indicate an error
callback(
new Error('no work done');
);// or return actual result
callback(null, {
variables: {
numberVar: newNumber
}
});
});
```### Work with async Function
ES6 style async/await to implement work is fully supported:
```javascript
// implement work via a Promise returning async function
worker.subscribe('work:B', async function(context) {// await async increment
var newNumber = await increment(context.variables.numberVar);// indicate an error
throw new Error('no work done');// or return actual result
return {
variables: {
numberVar: newNumber
}
};
});
```## Trigger BPMN Errors
You may indicate BPMN errors to trigger business defined exception handling:
```javascript
worker.subscribe('work:B', async function(context) {// trigger business aka BPMN errors
return {
errorCode: 'some-bpmn-error'
};
});
```## Task Locks
You may configure the initial lock time (defaults to 10 seconds) on worker registration.
At the same time you may use the method `extendLock`, provided via the task context,
to increase the lock time while the worker is busy.```javascript
// configure three seconds as initial lock time
worker.subscribe('work:B', {
lockDuration: 3000,
variables: [ 'a' ]
}, async function(context) {var extendLock = context.extendLock;
// extend the lock for another five seconds
await extendLock(5000);// complete task
return {};
});
```Read more about external task locking in the [Camunda Documentation](https://docs.camunda.org/manual/latest/user-guide/process-engine/external-tasks/).
## Authentication
We provide middlewares for basic auth as well as token based authentication.
### Basic Auth
Provide your client credentials via the [`BasicAuth`](./lib/basic-auth.js) middleware:
```javascript
var worker = Worker(engineEndpoint, {
use: [
BasicAuth('walt', 'SECRET_PASSWORD')
]
};
```### Token Authentication
Provide your tokens via the [`Auth`](./lib/auth.js) middleware:
```javascript
var worker = Worker(engineEndpoint, {
use: [
Auth('Bearer', 'BEARER_TOKEN')
]
};
```### Custom Made
To support custom authentication options add additional request headers to authenticate your task worker via the `requestOptions` configuration:
```javascript
var worker = Worker(engineEndpoint, {
requestOptions: {
headers: {
Hello: 'Authenticated?'
}
}
})
```## Logging
We employ [debug](https://www.npmjs.com/package/debug) for logging.
Use the [`Logger` extension](./lib/logger.js) in combination with `DEBUG=*` to capture a full trace of what's going on under the hood:
```bash
DEBUG=* node start-workers.js
```## Task Fetching
Task fetching is controlled by two configuration properties:
* `maxTasks` - maximum number of tasks to be fetched and locked with a single poll
* `pollingInterval` - interval in milliseconds between pollsYou may configure both properties on worker creation and at run-time:
```javascript
var worker = Worker(engineEndpoint, {
maxTasks: 2,
pollingInterval: 1500
});// dynamically increase max tasks
worker.configure({
maxTasks: 5
});
```This way you can configure the task fetching behavior both statically and dynamically.
Roll your own middleware to dynamically configure the worker instance or let
the [`Backoff` middleware](./lib/backoff.js) take care of it.As an alternative to configuring these values you may [stop and re-start](#worker-life-cycle)
the Worker instance as needed, too.## Worker Life-Cycle
Per default the worker instance will start to poll for work immediately.
Configure this behavior via the `autoPoll` option and start, stop and re-start
the instance programatically if you need to:```javascript
var worker = Worker(engineEndpoint, {
autoPoll: false
});// manually start polling
worker.start();// stop later on
await worker.stop();// re-start at some point in time
worker.start();
```## Extend via Plug-ins
A worker may be extended via the `use` config parameter.
```javascript
Worker(engineEndpoint, {
use: [
Logger,
Backoff,
Metrics,
BasicAuth('Walt', 'SECRET_PASSWORD')
]
});
```#### Existing Extensions
* [`Logger`](./lib/logger.js) - adds verbose logging of what is going on
* [`Backoff`](./lib/backoff.js) - dynamically adjust poll times based on Camunda REST api availability, fetched tasks and poll processing times
* [`Metrics`](./lib/metrics.js) - collect and periodically log utilization metrics
* [`BasicAuth`](./lib/basic-auth.js) - authorize against REST api with username + password
* [`Auth`](./lib/auth.js) - authorize against REST api with arbitrary tokens## Variable Serialization
Variables, when being read, modified and passed back on taks completion will preserve
their serialized form (indicated via `valueInfo` as documented in the [Camunda REST API documentation](https://docs.camunda.org/manual/latest/reference/rest/external-task/post-complete/)). Newly added variables will be serialized without `valueInfo` using the types `String`, `Date`, `Boolean`, `Json` or `Double` as appropriate.You may wrap variables with `SerializedVariable` if you would like to take full control over variable serialization:
```javascript
var Serialized = require('camunda-worker-node/lib/serialized-variable');worker.subscribe('shop:create-customer', async function(context) {
return {
variables: {
// wrap user to indicate it is already serialized
customer: Serialized({
type: 'Object',
value: JSON.stringify({
name: 'Hugo'
}),
valueInfo: {
serializationDataFormat: 'application/json',
objectTypeName: 'my.example.Customer'
}
})
}
};
});
```## Dynamically Unregister a Work Subscription
It is possible to dynamically unregister a work subscription any time.
```javascript
var subscription = worker.subscribe('someTopic', async function(context) {
// do work
console.log('doing work!');
});// later
subscription.remove();
```## Installation
```bash
npm i --save camunda-worker-node
```## Develop
Install dependencies:
```bash
npm install
```Lint and run all tests:
```bash
DEBUG=worker* npm run all
```__Note:__ You need a Camunda BPM REST API exposed on `localhost:8080/engine-rest` for the tests to pass. An easy way to get it up running is [via Docker](https://github.com/camunda/docker-camunda-bpm-platform#get-started).
## Related
* [External task documentation](https://docs.camunda.org/manual/latest/user-guide/process-engine/external-tasks/)
* [Official external task client](https://github.com/camunda/camunda-external-task-client-js)## License
MIT