Installation
Set up Flowlib with Express, NestJS, or Next.js.
The fastest way to install Flowlib is with the CLI:
npx flowlib-cli initIt 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/expressnpm install @flowlib/core @flowlib/expressyarn add @flowlib/core @flowlib/expressbun add @flowlib/core @flowlib/expresspnpm add @flowlib/core @flowlib/nestjsnpm install @flowlib/core @flowlib/nestjsyarn add @flowlib/core @flowlib/nestjsbun add @flowlib/core @flowlib/nestjspnpm add @flowlib/core @flowlib/nextjs @flowlib/uinpm install @flowlib/core @flowlib/nextjs @flowlib/uiyarn add @flowlib/core @flowlib/nextjs @flowlib/uibun add @flowlib/core @flowlib/nextjs @flowlib/uiSet environment variables
DATABASE_URL=file:./dev.db
FLOWLIB_ENCRYPTION_KEY= # Generate with: npx flowlib-cli secretDATABASE_URL=file:./dev.db
FLOWLIB_ENCRYPTION_KEY= # Generate with: npx flowlib-cli secretDATABASE_URL=file:./dev.db
FLOWLIB_ENCRYPTION_KEY= # Generate with: npx flowlib-cli secretSet up the backend
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');
});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:
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:
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:
{
"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/uiThen mount the editor:
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/uiThen mount the editor:
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:
'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 migrateStart the server
npx tsx index.tsThe API is now live at http://localhost:3000/flowlib. Open your frontend app to access the visual flow editor.
npx nest start --watchThe API is now live at http://localhost:3000/flowlib. Open your frontend app to access the visual flow editor.
npx next devOpen http://localhost:3000/flowlib — the visual flow editor is ready.
Configuration
All frameworks accept the full FlowlibConfig object:
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! },
},
],
}),
);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 {}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;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! },
},
],
});{
"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.
import { createFlowlibRouter } from '@flowlib/express';
import { flowlibConfig } from './flowlib.config';
app.use('/flowlib', await createFlowlibRouter(flowlibConfig));import { Module } from '@nestjs/common';
import { FlowlibModule } from '@flowlib/nestjs';
import { flowlibConfig } from './flowlib.config';
@Module({
imports: [FlowlibModule.forRoot(flowlibConfig)],
})
export class AppModule {}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:
'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):
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
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',
},