Integrating BPMN Server into your application#
BPMN Server is an embeddable execution engine, not a stand-alone process server. Your application hosts the engine, owns authentication and business data, and provides the business behavior invoked by workflows. Use bpmn-web if you also want a packaged UI, modeler and HTTP API.
This guide follows the 3.5 monorepo and its sample application, rather than the retired WorkflowApp layout.
Choose your integration#
| Option | When to choose it | Entry point |
|---|---|---|
| Use the starter | You want a complete web application with tasks, modeling, and admin | npx bpmn-server init my-app |
| Embed the engine | You already have a Node.js server/UI and want to control its lifecycle | BPMNServer.create(...), BPMNAPI |
| Run the engine apart | Production: the host serves pages and APIs, and the engine holds your code | Ways to run it |
| Call a remote host | Another application already runs BPMN Server and exposes its API | bpmn-client |
| Integrate an external HTTP service | A BPMN task must call a REST service described by OpenAPI | Connectors |
Start with Installation. For a local evaluation without MongoDB, npx bpmn-server init my-app --memory creates a starter that uses in-memory storage.
Component responsibilities#
sequenceDiagram
actor User
participant App as Hosting application/UI
participant API as BPMN API
participant Engine as BPMN execution
participant Delegate as AppDelegate/services
participant DB as Business database
participant Store as Workflow store
User->>App: Start or complete task
App->>App: Authenticate and authorize
App->>API: Start/invoke with trusted principal
API->>Engine: Execute BPMN model
Engine->>Delegate: Invoke service task or event
Delegate->>DB: Apply business operation
DB-->>Delegate: Result
Delegate-->>Engine: Output
Engine->>Store: Save workflow state
Engine-->>API: Execution result
API-->>App: Instance or task state
App-->>User: Updated UI
| Concern | Owner |
|---|---|
| User interface, sessions, authentication | Hosting application (or bpmn-web) |
| Application authorization and business rules | Hosting application, with engine-level task access checks |
| Process definitions, instances, tokens and task state | BPMN Server and its configured workflow store |
| Business records such as orders, claims, and employees | Hosting application's business services/database |
| User/task interactions | Application calls BPMNAPI.engine with a trusted principal |
| Service tasks and domain operations | Application services exposed by the delegate |
| External OpenAPI operations | Connector registry and configured connector credentials |
A process can store a business identifier such as claimId and call the application to read or update the claim. Do not depend on workflow variables as the authoritative store for business transactions.
Start with the sample application#
The source is at apps/sampleApp/. The starter command copies its application structure:
my-app/
├── processes/ # BPMN and DMN definitions
├── connectors/
│ ├── appServices.ts # Code called by service tasks
│ └── *.json # Editor element templates
├── delegates/
│ └── appDelegate.ts # App-specific hooks and service provider
├── settings/
│ └── config.ts # Engine/web configuration
├── index.ts # Hosts bpmn-web
└── .env # DB, port and API keys (keep private)
index.ts starts the web host using start from bpmn-web. This is the same pattern as the checked-in sample:
import { start } from 'bpmn-web';
import { config, connectorsPath } from './settings/config.js';
import { AppDelegate } from './delegates/appDelegate.js';
await start({
config: config(),
appDelegate: AppDelegate,
elementTemplatesPath: connectorsPath,
api: true,
ui: true
});
Configure api or ui to false when the host should serve only the other surface. The sample also uses setupOnly and console modes. The host, not the engine package alone, serves HTTP routes.
Embed only the engine#
For an existing Node.js host, create the engine with application configuration and keep the reference for its lifetime:
import { BPMNServer, BPMNAPI, Logger } from 'bpmn-server';
import { configuration } from './configuration.js';
const server = await BPMNServer.create(
configuration,
new Logger({ toConsole: false })
);
const api = new BPMNAPI(server);
// Pass an authenticated principal to runtime API calls.
// For example: await api.engine.start('Leave Application', { days: 3 }, user);
// At application shutdown:
await server.shutdown();
The application supplies configuration (including appDelegate, definitionsPath, and storage). See Setup / Configuration, Invoking workflows, and Security. Use BPMNAdminAPI only for model administration; normal runtime code should use BPMNAPI.
Implement service tasks in application code#
In the starter, connectors/appServices.ts exports AppServices. One of its methods is:
class AppServices {
constructor(private readonly delegate: unknown) {}
async greet({ name }: { name?: string }, context: any) {
return `Hello ${name || 'there'}, from ${context.instance?.name}`;
}
}
The application delegate exposes the service provider to the engine:
import { WebAppDelegate } from 'bpmn-web';
import { AppServices } from '../connectors/appServices.js';
export class AppDelegate extends WebAppDelegate {
private services?: AppServices;
async getServicesProvider(_context: unknown) {
return this.services ??= new AppServices(this);
}
}
These are abbreviated examples of the starter's real implementation. In the sample, the delegate also inherits web-specific functionality (notifications/startup), implements lifecycle callbacks, and can load data objects. The greet method is wired by a BPMN service task and its input/output mappings, not by copying TypeScript into the BPMN XML. See Service tasks and Scripting.
For complex business rules, prefer maintainable, testable application services. Use DMN for decision tables, and connectors for REST operations that do not need custom application code.
Events and data objects#
An app delegate can subscribe to execution lifecycle hooks such as executionStarted, executionEnded and executionEvent. Keep side effects idempotent where a retry or recovery might re-enter work.
To load external data into workflow variables, implement loadDataObject(name, item) on the delegate and associate the BPMN data object with the element that needs it. See Data objects for lifecycle and persistence details.
Persistence and deployment#
BPMN Server supports MongoDB by default, SqliteDocumentStore for small persistent installs on Node.js 22.5+, and MemoryDocumentStore for tests and demos. Custom adapters use the documentStore configuration port. Locking, timer scheduling, and shutdown are described in Setup.
Before exposing an instance to real users, configure the host's authentication, task authorization, connector keys, persistence, backups, and process recovery. See Security and Advanced topics.