@quanticjs/workflow-quanticflow
v8.0.0
Published
QuanticFlow workflow engine adapter for @quanticjs/workflow — supports callback and event-driven service task execution
Maintainers
Readme
@quanticjs/workflow-quanticflow
QuanticFlow workflow engine adapter for @quanticjs/workflow. Connects any QuanticJS application to a standalone QuanticFlow instance via REST API.
Supports two modes for service task execution:
| Mode | Transport | How it works |
|---|---|---|
| Callback | HTTP webhook | QuanticFlow POSTs to your app's /workflow-callback/service-task endpoint |
| Event | Redis Streams | Your app consumes ServiceTaskStartedEvent from Redis and signals back via REST |
Both modes can run simultaneously (mode: 'both').
Install
npm install @quanticjs/workflow-quanticflowQuick Start
import { Module } from '@nestjs/common';
import { QuanticFlowWorkflowModule } from '@quanticjs/workflow-quanticflow';
import { QuanticWorkflowModule } from '@quanticjs/workflow';
@Module({
imports: [
// 1. Register the QuanticFlow engine adapter
QuanticFlowWorkflowModule.forRoot({
url: 'http://quanticflow:3000',
serviceTaskHandling: {
mode: 'both', // 'callback' | 'event' | 'both'
callbackSecret: process.env.CALLBACK_SECRET,
},
}),
// 2. Wire the @Workflow decorator into the CQRS pipeline
QuanticWorkflowModule.forRoot(),
],
})
export class AppModule {}Now any command decorated with @Workflow will be routed to QuanticFlow:
import { Workflow } from '@quanticjs/core';
@Workflow('expense-approval')
@Validate(CreateExpenseValidator)
export class CreateExpenseCommand {
constructor(
readonly amount: number,
readonly category: string,
) {}
}Service Task Handlers
Register handlers that execute when QuanticFlow reaches a service task node:
import { Injectable } from '@nestjs/common';
import { ServiceTaskHandler, ServiceTaskContext, ServiceTaskResult } from '@quanticjs/workflow-quanticflow';
@Injectable()
export class SendEmailHandler implements ServiceTaskHandler {
constructor(private readonly mailer: MailerService) {}
async execute(context: ServiceTaskContext): Promise<ServiceTaskResult> {
await this.mailer.send({
to: context.variables.recipientEmail,
subject: `Process ${context.correlationId} update`,
});
return { signal: 'email-sent', data: { sentAt: new Date() } };
}
}Register handlers in your module — the registry resolves them by name from the DI container:
@Module({
providers: [SendEmailHandler],
})
export class NotificationsModule {}The handler name in the BPMN definition must match the class name (e.g., SendEmailHandler).
Configuration
QuanticFlowWorkflowModule.forRoot({
// Required
url: 'http://quanticflow:3000',
// Optional
requestTimeout: 10000, // HTTP timeout in ms (default: 10000)
serviceTaskHandling: {
mode: 'callback', // 'callback' | 'event' | 'both'
// Callback mode options
callbackSecret: 'hmac-secret', // HMAC-SHA256 signature verification
// Event mode options (requires ioredis + @quanticjs/redis)
streamKey: 'quantic:events:services', // Redis stream (default)
consumerGroup: 'my-app-tasks', // Consumer group name
consumerName: 'worker-1', // Consumer name (default: consumer-{pid})
},
})Using the Client Directly
The QuanticFlowClient is exported for direct REST API access beyond the WorkflowEngine interface:
import { QuanticFlowClient } from '@quanticjs/workflow-quanticflow';
@Injectable()
export class WorkflowDashboardService {
constructor(private readonly qf: QuanticFlowClient) {}
async getMyTasks(userId: string) {
return this.qf.listTasks({ userId, status: 'pending' });
}
async approveTask(taskId: string) {
return this.qf.executeTaskAction(taskId, 'approve', { comment: 'Looks good' });
}
}Client Methods
| Method | Endpoint | Description |
|---|---|---|
| startInstance(definitionId, variables?, correlationId?) | POST /workflow/instances | Start a workflow |
| signalInstance(instanceId, signal, data?) | POST /workflow/instances/:id/signal | Send signal |
| abortInstance(instanceId) | POST /workflow/instances/:id/abort | Abort workflow |
| getInstance(instanceId) | GET /workflow/instances/:id | Get instance detail |
| updateVariables(instanceId, variables) | PUT /workflow/instances/:id/variables | Update variables |
| listTasks(filters?) | GET /workflow/tasks | List tasks |
| getTask(taskId) | GET /workflow/tasks/:id | Get task detail |
| claimTask(taskId) | POST /workflow/tasks/:id/claim | Claim task |
| executeTaskAction(taskId, action, data?) | POST /workflow/tasks/:id/action | Execute action |
Architecture
┌──────────────────────────────────────────────┐
│ Your NestJS App │
│ │
│ @Workflow('expense') │
│ CreateExpenseCommand ──▶ WorkflowBehavior │
│ │ │
│ ┌─────────▼──────────┐ │
│ │ QuanticFlowWorkflow │ │
│ │ Engine (HTTP) │ │
│ └─────────┬──────────┘ │
│ │ │
│ ┌───────────────────────────┼────────────┐ │
│ │ Service Task Handling │ │ │
│ │ │ │ │
│ │ ┌─────────────┐ ┌──────▼─────────┐ │ │
│ │ │ Callback │ │ Event │ │ │
│ │ │ Controller │ │ Consumer │ │ │
│ │ │ (webhook) │ │ (Redis Stream)│ │ │
│ │ └──────┬──────┘ └──────┬─────────┘ │ │
│ │ │ │ │ │
│ │ ▼ ▼ │ │
│ │ ServiceTaskHandlerRegistry │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ SendEmailHandler (your code) │ │
│ └────────────────────────────────────────┘ │
└──────────────────────────────────────────────┘
│
│ HTTP / Redis
▼
┌──────────────────┐
│ QuanticFlow │
│ (standalone) │
└──────────────────┘License
MIT
