Skip to content

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.

Next steps#

Comments