Skip to Content
DocumentationBuilding with LoopstackπŸš€ Running Workflows

πŸš€ Running Workflows

This guide explains how to trigger Loopstack workflows programmatically from within your NestJS application, enabling you to integrate automation runs into your existing API endpoints, scheduled jobs, or event handlers.

Overview

While workflows can be manually triggered through the Loopstack Studio interface, you often need to start automation runs programmatically in response to:

  • External API requests
  • Webhook events
  • Scheduled cron jobs
  • Internal application events
  • Batch processing tasks

Loopstack provides the RunService to execute workflows programmatically from anywhere in your NestJS application.

Example

Step 1: Create a Controller

Create a new controller or use an existing one to add an endpoint that will trigger your workflow:

src/app.controller.ts:

import { Controller, Post, Body } from '@nestjs/common'; import { Public } from '@loopstack/shared'; import { RunService } from '@loopstack/core'; import { MyWorkflow } from './my-module/my-workflow.workflow'; import { MyWorkspace } from './my-module/my-workspace'; @Controller() export class AppController { constructor(private readonly runService: RunService) {} @Public() @Post('run-my-workflow') async runMyWorkflow(@Body() payload: any) { // Define the user ID for whom the automation should be executed const userId = 'user-123'; await this.runService.run( MyWorkflow.name, MyWorkspace.name, payload, userId, ); return { message: 'Automation run is queued.' }; } }

In this file:

  • We inject the RunService through the constructor
  • We create a POST endpoint that accepts a payload
  • We call runService.run() with the workflow name, workspace name, initial payload, and user ID
  • We use @Public() decorator to make this endpoint accessible without authentication (remove this if you need authentication)

Step 2: Understanding the RunService Parameters

The runService.run() method accepts four parameters:

await this.runService.run( workflowName: string, // The class name of your workflow workspaceName: string, // The class name of your workspace payload: any, // Data to pass to the workflow userId: string, // The user ID for workflow execution context );

Advanced Usage Examples

Example 1: Webhook Handler

Trigger a workflow when receiving a webhook from an external service:

import { Controller, Post, Body, Headers } from '@nestjs/common'; import { Public } from '@loopstack/shared'; import { RunService } from '@loopstack/core'; import { ProcessWebhookWorkflow } from './workflows/process-webhook.workflow'; import { MainWorkspace } from './main-workspace'; @Controller('webhooks') export class WebhookController { constructor(private readonly runService: RunService) {} @Public() @Post('stripe') async handleStripeWebhook( @Body() webhookData: any, @Headers('stripe-signature') signature: string, ) { // Verify webhook signature here (implementation not shown) await this.runService.run( ProcessWebhookWorkflow.name, MainWorkspace.name, { source: 'stripe', event: webhookData, signature: signature, receivedAt: new Date().toISOString(), }, webhookData.userId, ); return { received: true }; } }

Example 2: 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 { RunService } from '@loopstack/core'; import { DailyReportWorkflow } from './workflows/daily-report.workflow'; import { ReportsWorkspace } from './reports-workspace'; @Injectable() export class TaskScheduler { constructor(private readonly runService: RunService) {} @Cron(CronExpression.EVERY_DAY_AT_9AM) async generateDailyReports() { // Get list of users who need reports const users = await this.getUsersWithReportsEnabled(); for (const user of users) { await this.runService.run( DailyReportWorkflow.name, ReportsWorkspace.name, { reportDate: new Date().toISOString(), reportType: 'daily', }, user.id, ); } } private async getUsersWithReportsEnabled() { // Implementation to fetch users return []; } }

Example 3: Processing Form Submissions

Trigger a workflow when a user submits a form:

import { Controller, Post, Body, UseGuards } from '@nestjs/common'; import { AuthGuard } from '@nestjs/passport'; import { CurrentUser } from './decorators/current-user.decorator'; import { RunService } from '@loopstack/core'; import { ProcessApplicationWorkflow } from './workflows/process-application.workflow'; import { ApplicationsWorkspace } from './applications-workspace'; interface ApplicationFormData { firstName: string; lastName: string; email: string; resume: string; coverLetter: string; } @Controller('applications') export class ApplicationsController { constructor(private readonly runService: RunService) {} @UseGuards(AuthGuard('jwt')) @Post('submit') async submitApplication( @Body() formData: ApplicationFormData, @CurrentUser() user: any, ) { await this.runService.run( ProcessApplicationWorkflow.name, ApplicationsWorkspace.name, { applicantData: formData, submittedAt: new Date().toISOString(), source: 'web-form', }, user.id, ); return { message: 'Application submitted successfully', status: 'processing', }; } }

Example 4: Batch Processing

Process multiple items by triggering workflows in batch:

import { Injectable } from '@nestjs/common'; import { RunService } from '@loopstack/core'; import { ProcessOrderWorkflow } from './workflows/process-order.workflow'; import { OrdersWorkspace } from './orders-workspace'; @Injectable() export class OrderProcessingService { constructor(private readonly runService: RunService) {} async processPendingOrders() { const pendingOrders = await this.getPendingOrders(); // Process orders in parallel const promises = pendingOrders.map(order => this.runService.run( ProcessOrderWorkflow.name, OrdersWorkspace.name, { orderId: order.id, orderData: order, priority: order.priority, }, order.userId, ) ); await Promise.all(promises); return { processed: pendingOrders.length }; } private async getPendingOrders() { // Implementation to fetch pending orders return []; } }
Last updated on