Running Workflows Programmatically
While workflows can be triggered through the Loopstack Studio interface, you can also start them programmatically from within your NestJS application.
Overview
Use the WorkflowRunner to execute workflows in response to:
- External API requests
- Webhook events
- Scheduled cron jobs
- Internal application events
- Batch processing tasks
Basic Example
Create a Controller
import { Body, Controller, Post } from '@nestjs/common';
import { WorkflowRunner } from '@loopstack/core';
import { DefaultApp } from './default.app';
import { MyWorkflow } from './workflows/my.workflow';
@Controller()
export class AppController {
constructor(private readonly workflowRunner: WorkflowRunner) {}
@Post('run-my-workflow')
async runMyWorkflow(@Body() payload: any) {
const userId = // define a user id to run the workflow
await this.workflowRunner.run(MyWorkflow, payload, {
app: DefaultApp,
userId,
});
return { message: 'Workflow run is queued.' };
}
}WorkflowRunner Methods
// Async (queued via BullMQ)
await this.workflowRunner.run(
workflow, // Workflow class reference
args, // Data passed as workflow args (type-safe)
{
app, // App class reference
userId, // User ID for execution context
},
);
// Sync (inline execution, awaits result)
await this.workflowRunner.runSync(
workflow, // Workflow class reference
args, // Data passed as workflow args (type-safe)
{
app, // App class reference
userId, // User ID for execution context
stateless, // Optional: skip persistence (default: false)
},
);Advanced Examples
Webhook Handler
Trigger a workflow when receiving a webhook:
import { Body, Controller, Headers, Post } from '@nestjs/common';
import { WorkflowRunner } from '@loopstack/core';
@Controller('webhooks')
export class WebhookController {
constructor(private readonly workflowRunner: WorkflowRunner) {}
@Post('stripe')
async handleStripeWebhook(@Body() webhookData: any, @Headers('stripe-signature') signature: string) {
await this.workflowRunner.run(
ProcessWebhookWorkflow,
{
source: 'stripe',
event: webhookData,
receivedAt: new Date().toISOString(),
},
{
app: MainApp,
userId: webhookData.userId,
},
);
return { received: true };
}
}Scheduled Task
Execute a workflow on a schedule using NestJS’s @Cron decorator:
import { Injectable } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';
import { WorkflowRunner } from '@loopstack/core';
@Injectable()
export class TaskScheduler {
constructor(private readonly workflowRunner: WorkflowRunner) {}
@Cron(CronExpression.EVERY_DAY_AT_9AM)
async generateDailyReports() {
const users = await this.getUsersWithReportsEnabled();
for (const user of users) {
await this.workflowRunner.run(
DailyReportWorkflow,
{
reportDate: new Date().toISOString(),
reportType: 'daily',
},
{
app: ReportsApp,
userId: user.id,
},
);
}
}
}Batch Processing
Process multiple items by triggering workflows in parallel:
import { Injectable } from '@nestjs/common';
import { WorkflowRunner } from '@loopstack/core';
@Injectable()
export class OrderProcessingService {
constructor(private readonly workflowRunner: WorkflowRunner) {}
async processPendingOrders() {
const pendingOrders = await this.getPendingOrders();
const promises = pendingOrders.map((order) =>
this.workflowRunner.run(
ProcessOrderWorkflow,
{
orderId: order.id,
orderData: order,
},
{
app: OrdersApp,
userId: order.userId,
},
),
);
await Promise.all(promises);
return { processed: pendingOrders.length };
}
}Last updated on