# Installation (/docs/installation)





The fastest way to install Flowlib is with the CLI:

```bash
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 [#getting-started]

<Steps>
  <Step>
    ### Install packages [#install-packages]

    <Tabs groupId="framework" items="['Express', 'NestJS', 'Next.js']">
      <Tab value="Express">
        <Tabs items="['pnpm', 'npm', 'yarn', 'bun']">
          <Tab value="pnpm">
            ```bash
            pnpm add @flowlib/core @flowlib/express
            ```
          </Tab>

          <Tab value="npm">
            ```bash
            npm install @flowlib/core @flowlib/express
            ```
          </Tab>

          <Tab value="yarn">
            ```bash
            yarn add @flowlib/core @flowlib/express
            ```
          </Tab>

          <Tab value="bun">
            ```bash
            bun add @flowlib/core @flowlib/express
            ```
          </Tab>
        </Tabs>
      </Tab>

      <Tab value="NestJS">
        <Tabs items="['pnpm', 'npm', 'yarn', 'bun']">
          <Tab value="pnpm">
            ```bash
            pnpm add @flowlib/core @flowlib/nestjs
            ```
          </Tab>

          <Tab value="npm">
            ```bash
            npm install @flowlib/core @flowlib/nestjs
            ```
          </Tab>

          <Tab value="yarn">
            ```bash
            yarn add @flowlib/core @flowlib/nestjs
            ```
          </Tab>

          <Tab value="bun">
            ```bash
            bun add @flowlib/core @flowlib/nestjs
            ```
          </Tab>
        </Tabs>
      </Tab>

      <Tab value="Next.js">
        <Tabs items="['pnpm', 'npm', 'yarn', 'bun']">
          <Tab value="pnpm">
            ```bash
            pnpm add @flowlib/core @flowlib/nextjs @flowlib/ui
            ```
          </Tab>

          <Tab value="npm">
            ```bash
            npm install @flowlib/core @flowlib/nextjs @flowlib/ui
            ```
          </Tab>

          <Tab value="yarn">
            ```bash
            yarn add @flowlib/core @flowlib/nextjs @flowlib/ui
            ```
          </Tab>

          <Tab value="bun">
            ```bash
            bun add @flowlib/core @flowlib/nextjs @flowlib/ui
            ```
          </Tab>
        </Tabs>
      </Tab>
    </Tabs>
  </Step>

  <Step>
    ### Set environment variables [#set-environment-variables]

    <Tabs groupId="framework" items="['Express', 'NestJS', 'Next.js']">
      <Tab value="Express">
        ```bash title=".env"
        DATABASE_URL=file:./dev.db
        FLOWLIB_ENCRYPTION_KEY=   # Generate with: npx flowlib-cli secret
        ```
      </Tab>

      <Tab value="NestJS">
        ```bash title=".env"
        DATABASE_URL=file:./dev.db
        FLOWLIB_ENCRYPTION_KEY=   # Generate with: npx flowlib-cli secret
        ```
      </Tab>

      <Tab value="Next.js">
        ```bash title=".env.local"
        DATABASE_URL=file:./dev.db
        FLOWLIB_ENCRYPTION_KEY=   # Generate with: npx flowlib-cli secret
        ```
      </Tab>
    </Tabs>
  </Step>

  <Step>
    ### Set up the backend [#set-up-the-backend]

    <Tabs groupId="framework" items="['Express', 'NestJS', 'Next.js']">
      <Tab value="Express">
        ```ts title="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');
        });
        ```
      </Tab>

      <Tab value="NestJS">
        ```ts title="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`).
      </Tab>

      <Tab value="Next.js">
        Create the catch-all API route handler:

        ```ts title="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:

        ```ts title="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`:

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

        <Callout type="info">
          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.
        </Callout>
      </Tab>
    </Tabs>
  </Step>

  <Step>
    ### Add the frontend [#add-the-frontend]

    <Tabs groupId="framework" items="['Express', 'NestJS', 'Next.js']">
      <Tab value="Express">
        The visual flow editor runs as a separate React app. Install `@flowlib/ui` in your frontend project:

        ```bash
        pnpm add @flowlib/ui
        ```

        Then mount the editor:

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

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

      <Tab value="NestJS">
        The visual flow editor runs as a separate React app. Install `@flowlib/ui` in your frontend project:

        ```bash
        pnpm add @flowlib/ui
        ```

        Then mount the editor:

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

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

      <Tab value="Next.js">
        Mount the frontend in a catch-all page route:

        ```tsx title="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} />;
        }
        ```

        <Callout type="info">
          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.
        </Callout>
      </Tab>
    </Tabs>
  </Step>

  <Step>
    ### Generate and migrate the database [#generate-and-migrate-the-database]

    ```bash
    npx flowlib-cli generate
    npx flowlib-cli migrate
    ```
  </Step>

  <Step>
    ### Start the server [#start-the-server]

    <Tabs groupId="framework" items="['Express', 'NestJS', 'Next.js']">
      <Tab value="Express">
        ```bash
        npx tsx index.ts
        ```

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

      <Tab value="NestJS">
        ```bash
        npx nest start --watch
        ```

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

      <Tab value="Next.js">
        ```bash
        npx next dev
        ```

        Open `http://localhost:3000/flowlib` — the visual flow editor is ready.
      </Tab>
    </Tabs>
  </Step>
</Steps>

***

## Configuration [#configuration]

All frameworks accept the full [`FlowlibConfig`](/docs/reference/configuration) object:

<Tabs groupId="framework" items="['Express', 'NestJS', 'Next.js']">
  <Tab value="Express">
    ```ts title="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! },
          },
        ],
      }),
    );
    ```
  </Tab>

  <Tab value="NestJS">
    ```ts title="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 {}
    ```
  </Tab>

  <Tab value="Next.js">
    ```ts title="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;
    ```

    ```ts title="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! },
        },
      ],
    });
    ```

    ```json title="vercel.json"
    {
      "crons": [{ "path": "/api/flowlib/cron", "schedule": "* * * * *" }]
    }
    ```
  </Tab>
</Tabs>

***

## With plugins [#with-plugins]

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

<Tabs groupId="framework" items="['Express', 'NestJS', 'Next.js']">
  <Tab value="Express">
    ```ts title="index.ts"
    import { createFlowlibRouter } from '@flowlib/express';
    import { flowlibConfig } from './flowlib.config';

    app.use('/flowlib', await createFlowlibRouter(flowlibConfig));
    ```
  </Tab>

  <Tab value="NestJS">
    ```ts title="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 {}
    ```
  </Tab>

  <Tab value="Next.js">
    ```ts title="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:

    ```tsx title="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](/docs/plugins/authentication) and [Access Control Plugin](/docs/plugins/access-control) for details.
  </Tab>
</Tabs>

***

## NestJS extras [#nestjs-extras]

### Async configuration [#async-configuration]

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

```ts title="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 [#accessing-the-core-instance]

```ts title="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 [#postgresql]

Any framework can use PostgreSQL by changing the database config:

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