Flowlib

Installation

Set up Flowlib with Express, NestJS, or Next.js.

The fastest way to install Flowlib is with the CLI:

npx flowlib-cli init

It detects your framework, installs packages, generates database schemas, and runs the initial migration. The manual steps below are for reference.


Getting Started

Install packages

pnpm add @flowlib/core @flowlib/express
npm install @flowlib/core @flowlib/express
yarn add @flowlib/core @flowlib/express
bun add @flowlib/core @flowlib/express
pnpm add @flowlib/core @flowlib/nestjs
npm install @flowlib/core @flowlib/nestjs
yarn add @flowlib/core @flowlib/nestjs
bun add @flowlib/core @flowlib/nestjs
pnpm add @flowlib/core @flowlib/nextjs @flowlib/ui
npm install @flowlib/core @flowlib/nextjs @flowlib/ui
yarn add @flowlib/core @flowlib/nextjs @flowlib/ui
bun add @flowlib/core @flowlib/nextjs @flowlib/ui

Set environment variables

.env
DATABASE_URL=file:./dev.db
FLOWLIB_ENCRYPTION_KEY=   # Generate with: npx flowlib-cli secret
.env
DATABASE_URL=file:./dev.db
FLOWLIB_ENCRYPTION_KEY=   # Generate with: npx flowlib-cli secret
.env.local
DATABASE_URL=file:./dev.db
FLOWLIB_ENCRYPTION_KEY=   # Generate with: npx flowlib-cli secret

Set up the backend

index.ts
import 'dotenv/config';
import express from 'express';
import { createFlowlibRouter } from '@flowlib/express';

const app = express();
app.use(express.json());

app.use(
  '/flowlib',
  await createFlowlibRouter({
    database: {
      type: 'sqlite',
      connectionString: process.env.DATABASE_URL!,
    },
    encryptionKey: process.env.FLOWLIB_ENCRYPTION_KEY,
  }),
);

app.listen(3000, () => {
  console.log('Flowlib API at http://localhost:3000/flowlib');
});
app.module.ts
import { Module } from '@nestjs/common';
import { FlowlibModule } from '@flowlib/nestjs';

@Module({
  imports: [
    FlowlibModule.forRoot({
      database: {
        type: 'sqlite',
        connectionString: process.env.DATABASE_URL!,
      },
      encryptionKey: process.env.FLOWLIB_ENCRYPTION_KEY,
    }),
  ],
})
export class AppModule {}

This registers the FLOWLIB_CORE provider (singleton), FlowlibService (injectable wrapper), and FlowlibController (REST endpoints under /flowlib).

Create the catch-all API route handler:

app/api/flowlib/[...flowlib]/route.ts
import { createFlowlibHandler } from '@flowlib/nextjs';

const handler = createFlowlibHandler({
  database: {
    type: 'sqlite',
    connectionString: process.env.DATABASE_URL!,
  },
  encryptionKey: process.env.FLOWLIB_ENCRYPTION_KEY,
});

export const GET = handler.GET;
export const POST = handler.POST;
export const PATCH = handler.PATCH;
export const PUT = handler.PUT;
export const DELETE = handler.DELETE;

Add a dedicated maintenance route for cron-driven background work on Vercel:

app/api/flowlib/cron/route.ts
import { createFlowlibCronHandler } from '@flowlib/nextjs';

const handleCron = createFlowlibCronHandler({
  database: {
    type: 'sqlite',
    connectionString: process.env.DATABASE_URL!,
  },
  encryptionKey: process.env.FLOWLIB_ENCRYPTION_KEY,
  triggers: {
    cronEnabled: true,
    webhookBaseUrl: 'https://your-app.com/api/flowlib',
  },
});

export const GET = handleCron;

Then configure one Vercel cron in vercel.json:

vercel.json
{
  "crons": [{ "path": "/api/flowlib/cron", "schedule": "* * * * *" }]
}

On Vercel, use one dedicated Flowlib cron route. That single route fans out into Flowlib maintenance tasks for the app: batch polling, paused-flow resumption, stale-run cleanup, and Flowlib cron triggers.

Add the frontend

The visual flow editor runs as a separate React app. Install @flowlib/ui in your frontend project:

pnpm add @flowlib/ui

Then mount the editor:

App.tsx
import { Flowlib } from '@flowlib/ui';
import '@flowlib/ui/styles';
import { flowlibConfig } from '../flowlib.config';

function App() {
  return <Flowlib config={flowlibConfig} />;
}

The visual flow editor runs as a separate React app. Install @flowlib/ui in your frontend project:

pnpm add @flowlib/ui

Then mount the editor:

App.tsx
import { Flowlib } from '@flowlib/ui';
import '@flowlib/ui/styles';
import { flowlibConfig } from '../flowlib.config';

function App() {
  return <Flowlib config={flowlibConfig} />;
}

Mount the frontend in a catch-all page route:

app/flowlib/[[...slug]]/page.tsx
'use client';

import dynamic from 'next/dynamic';
import '@flowlib/ui/styles';
import config from '../../flowlib.config';

const Flowlib = dynamic(() => import('@flowlib/ui').then((mod) => ({ default: mod.Flowlib })), {
  ssr: false,
});

export default function FlowlibPage() {
  return <Flowlib config={config} />;
}

Dynamic import with ssr: false is required because the flow editor uses browser APIs. flowlib.config.ts sets frontendPath so the router knows where it is mounted.

Generate and migrate the database

npx flowlib-cli generate
npx flowlib-cli migrate

Start the server

npx tsx index.ts

The API is now live at http://localhost:3000/flowlib. Open your frontend app to access the visual flow editor.

npx nest start --watch

The API is now live at http://localhost:3000/flowlib. Open your frontend app to access the visual flow editor.

npx next dev

Open http://localhost:3000/flowlib — the visual flow editor is ready.


Configuration

All frameworks accept the full FlowlibConfig object:

index.ts
import { createFlowlibRouter } from '@flowlib/express';

app.use(
  '/flowlib',
  await createFlowlibRouter({
    database: {
      type: 'sqlite',
      connectionString: process.env.DATABASE_URL!,
    },
    encryptionKey: process.env.FLOWLIB_ENCRYPTION_KEY,
    execution: {
      defaultTimeout: 60000,
      maxConcurrentExecutions: 10,
    },
    triggers: {
      cronEnabled: true,
      webhookBaseUrl: 'https://your-app.com/flowlib',
    },
    plugins: [
      // Add plugins here
    ],
    defaultCredentials: [
      {
        name: 'OpenAI',
        type: 'llm',
        provider: 'openai',
        authType: 'apiKey',
        config: { apiKey: process.env.OPENAI_API_KEY! },
      },
    ],
  }),
);
app.module.ts
import { FlowlibModule } from '@flowlib/nestjs';

@Module({
  imports: [
    FlowlibModule.forRoot({
      database: {
        type: 'sqlite',
        connectionString: process.env.DATABASE_URL!,
      },
      encryptionKey: process.env.FLOWLIB_ENCRYPTION_KEY,
      execution: {
        defaultTimeout: 60000,
        maxConcurrentExecutions: 10,
      },
      triggers: {
        cronEnabled: true,
        webhookBaseUrl: 'https://your-app.com/flowlib',
      },
      plugins: [
        // Add plugins here
      ],
      defaultCredentials: [
        {
          name: 'OpenAI',
          type: 'llm',
          provider: 'openai',
          authType: 'apiKey',
          config: { apiKey: process.env.OPENAI_API_KEY! },
        },
      ],
    }),
  ],
})
export class AppModule {}
app/api/flowlib/[...flowlib]/route.ts
import { createFlowlibHandler } from '@flowlib/nextjs';
import { auth } from '@flowlib/user-auth';
import { rbac } from '@flowlib/rbac';

const handler = createFlowlibHandler({
  database: {
    type: 'sqlite',
    connectionString: process.env.DATABASE_URL!,
  },
  encryptionKey: process.env.FLOWLIB_ENCRYPTION_KEY,
  execution: {
    defaultTimeout: 60000,
    maxConcurrentExecutions: 10,
  },
  triggers: {
    cronEnabled: true,
    webhookBaseUrl: 'https://your-app.com/api/flowlib',
  },
  plugins: [
    auth({
      /* Better Auth options */
    }),
    rbac(),
  ],
  defaultCredentials: [
    {
      name: 'OpenAI',
      type: 'llm',
      provider: 'openai',
      authType: 'apiKey',
      config: { apiKey: process.env.OPENAI_API_KEY! },
    },
  ],
});

export const GET = handler.GET;
export const POST = handler.POST;
export const PATCH = handler.PATCH;
export const PUT = handler.PUT;
export const DELETE = handler.DELETE;
app/api/flowlib/cron/route.ts
import { createFlowlibCronHandler } from '@flowlib/nextjs';
import { auth } from '@flowlib/user-auth';
import { rbac } from '@flowlib/rbac';

export const GET = createFlowlibCronHandler({
  database: {
    type: 'sqlite',
    connectionString: process.env.DATABASE_URL!,
  },
  encryptionKey: process.env.FLOWLIB_ENCRYPTION_KEY,
  execution: {
    defaultTimeout: 60000,
    maxConcurrentExecutions: 10,
  },
  triggers: {
    cronEnabled: true,
    webhookBaseUrl: 'https://your-app.com/api/flowlib',
  },
  plugins: [
    auth({
      /* Better Auth options */
    }),
    rbac(),
  ],
  defaultCredentials: [
    {
      name: 'OpenAI',
      type: 'llm',
      provider: 'openai',
      authType: 'apiKey',
      config: { apiKey: process.env.OPENAI_API_KEY! },
    },
  ],
});
vercel.json
{
  "crons": [{ "path": "/api/flowlib/cron", "schedule": "* * * * *" }]
}

With plugins

After adding plugins with database tables, run npx flowlib-cli generate and npx flowlib-cli migrate again.

index.ts
import { createFlowlibRouter } from '@flowlib/express';
import { flowlibConfig } from './flowlib.config';

app.use('/flowlib', await createFlowlibRouter(flowlibConfig));
app.module.ts
import { Module } from '@nestjs/common';
import { FlowlibModule } from '@flowlib/nestjs';
import { flowlibConfig } from './flowlib.config';

@Module({
  imports: [FlowlibModule.forRoot(flowlibConfig)],
})
export class AppModule {}
app/api/flowlib/[...flowlib]/route.ts
import { createFlowlibHandler } from '@flowlib/nextjs';
import { flowlibConfig } from '@/flowlib.config';

const handler = createFlowlibHandler(flowlibConfig);

export const GET = handler.GET;
export const POST = handler.POST;
export const PATCH = handler.PATCH;
export const PUT = handler.PUT;
export const DELETE = handler.DELETE;

Then mount the frontend:

app/flowlib/[[...slug]]/page.tsx
'use client';

import dynamic from 'next/dynamic';
import '@flowlib/ui/styles';
import config from '../../flowlib.config';

const Flowlib = dynamic(() => import('@flowlib/ui').then((m) => ({ default: m.Flowlib })), {
  ssr: false,
});

export default function FlowlibPage() {
  return <Flowlib config={config} />;
}

When the @flowlib/user-auth plugin is included in flowlib.config.ts, <Flowlib> automatically wraps itself with an auth gate — no extra wrapper component needed. The OAuth callback route is handled internally at <frontendPath>/oauth/callback.

See Authentication Plugin and Access Control Plugin for details.


NestJS extras

Async configuration

Use forRootAsync when you need to inject dependencies (e.g., ConfigService):

app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { FlowlibModule } from '@flowlib/nestjs';

@Module({
  imports: [
    ConfigModule.forRoot(),
    FlowlibModule.forRootAsync({
      inject: [ConfigService],
      useFactory: (config: ConfigService) => ({
        database: {
          type: 'postgresql' as const,
          connectionString: config.get('DATABASE_URL')!,
        },
        encryptionKey: config.get('FLOWLIB_ENCRYPTION_KEY')!,
      }),
    }),
  ],
})
export class AppModule {}

Accessing the core instance

my.service.ts
import { Injectable } from '@nestjs/common';
import { FlowlibService } from '@flowlib/nestjs';

@Injectable()
export class MyService {
  constructor(private readonly flowlib: FlowlibService) {}

  async runWorkflow(flowId: string, inputs: Record<string, unknown>) {
    const core = this.flowlib.getCore();
    return core.runs.start(flowId, inputs);
  }
}

PostgreSQL

Any framework can use PostgreSQL by changing the database config:

database: {
  type: 'postgresql',
  connectionString: 'postgresql://user:pass@localhost:5432/myapp',
},

On this page