Add to an Existing App
Already have a NestJS backend? Wire Loopstack into it directly. Starting fresh instead? loopstack create scaffolds all of this — module, config, a hello workflow, Docker Compose, and a .env — in one command.
1. Install the module
npm install @loopstack/loopstack-module2. Register the module
Add LoopstackModule to the imports in src/app.module.ts:
import { Module } from '@nestjs/common';
import { LoopstackModule } from '@loopstack/loopstack-module';
@Module({
imports: [LoopstackModule.forRoot()],
})
export class AppModule {}Enable shutdown hooks in src/main.ts so the workflow engine shuts down gracefully — on SIGTERM (a deploy, docker stop) in-flight transitions finish before the process exits:
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.enableShutdownHooks();
await app.listen(process.env.PORT ?? 3000);
}Add YAML asset bundling to nest-cli.json so workflow UI configs are included in the build:
{
"compilerOptions": {
"assets": ["**/*.yaml"]
}
}3. Provide Postgres & Redis
Loopstack needs a PostgreSQL and a Redis instance. There are two equally good ways to provide them:
Option A — Docker (quickest locally). The module ships a Compose file for Postgres and Redis:
docker compose -f node_modules/@loopstack/loopstack-module/docker-compose.yml up -dWant the visual Studio UI too? Start it alongside — it’s a separate, optional compose file:
docker compose -f node_modules/@loopstack/loopstack-module/docker-compose.studio.yml up -dOption B — Bring your own. Point the app at any existing Postgres and Redis (managed, hosted, or already running) via .env:
DATABASE_URL=postgres://user:password@host:5432/dbname
REDIS_URL=redis://host:6379CI and coding agents should use Option B (point the URLs at an available instance) and drive workflows from the CLI, not the browser-based Studio.
4. Write your first workflow
Create src/hello/hello.workflow.ts — a single class with one transition:
import { z } from 'zod';
import { BaseWorkflow, MessageDocument, Transition, Workflow } from '@loopstack/common';
import type { RunContext } from '@loopstack/common';
const InputSchema = z.object({
name: z.string().default('World'),
});
type InputArgs = z.infer<typeof InputSchema>;
@Workflow({
title: 'Hello World',
description: 'Greets you by name — replace this with your first real workflow.',
schema: InputSchema,
})
export class HelloWorkflow extends BaseWorkflow<InputArgs> {
@Transition({ from: 'start', to: 'end' })
async greet(_state: unknown, ctx: RunContext<InputArgs>) {
const greeting = `Hello, ${ctx.args.name}! 👋`;
await this.documentStore.save(MessageDocument, { role: 'assistant', text: greeting });
this.assignResult({ greeting });
}
}Create src/hello/hello.module.ts — @StudioApp groups workflows into an app:
import { Module } from '@nestjs/common';
import { StudioApp } from '@loopstack/common';
import { HelloWorkflow } from './hello.workflow';
@StudioApp({
title: 'My First App',
workflows: [HelloWorkflow],
})
@Module({
providers: [HelloWorkflow],
})
export class HelloModule {}Register it in src/app.module.ts:
import { Module } from '@nestjs/common';
import { LoopstackModule } from '@loopstack/loopstack-module';
import { HelloModule } from './hello/hello.module';
@Module({
imports: [LoopstackModule.forRoot(), HelloModule],
})
export class AppModule {}5. Run
npm run start:dev # http://localhost:3000Run the workflow from the terminal — the CLI talks to the local backend with no login:
npx @loopstack/cli run hello --arg name=YouIt streams each transition and the result live, and returns CI-friendly exit codes. Prefer a visual UI? Start Studio (step 3) and open http://localhost:5173 .
This first workflow is zero-config — no API keys needed. To make a workflow call an LLM, see AI Text Generation.
Next steps
- Core Concepts — workflows, tools, documents, and providers
- Creating Workflows — transitions, guards, state, and wait patterns
- AI Text Generation — add LLM calls to your workflows
zod version (reference)
Loopstack requires zod v4 — it uses the v4-only z.toJSONSchema() API to turn workflow input schemas into JSON Schema. npm 7+ installs it automatically as a peer dependency when you run npm install @loopstack/loopstack-module, so you don’t normally need to install it yourself. Older tutorials may show zod@^3; that won’t resolve against Loopstack’s peer constraint and npm install will fail with ERESOLVE.