Flow Runtime Package

npm downloads

The @hazeljs/flow-runtime package provides a standalone HTTP service (HazelApp) that runs @hazeljs/flow workflows. Expose flows via REST so other systems can start runs, tick, resume waiting runs, and read status and timeline—without implementing the server yourself.

Purpose

When you build workflows with @hazeljs/flow, you can either:

  • In-process — Use FlowEngine directly in your app (same process)
  • Standalone service — Run the flow-runtime process and call its HTTP API
  • Programmatic — Call runFlowRuntime({ flows, port, databaseUrl?, services }) from your app so the package owns the server and you just pass your flows

The flow-runtime package gives you:

  • REST API — Start runs, tick, resume, get run status and timeline, list flows
  • Recovery — On startup, picks up RUNNING runs and ticks them (with Prisma storage)
  • Optional Postgres — Pass databaseUrl for durable storage; omit for in-memory
  • Custom flows — Register your own flow definitions and optional services (logger, slack, etc.)

Architecture

graph LR
  A["Client / UI / Service"] -->|HTTP| B["Flow Runtime (HazelApp)"]
  B --> C["FlowEngine"]
  C --> D["In-Memory or Prisma"]
  D --> E["Postgres (optional)"]
  B --> F["Recovery (RUNNING runs)"]
  style A fill:#3b82f6,stroke:#60a5fa,color:#fff
  style B fill:#6366f1,stroke:#818cf8,color:#fff
  style C fill:#8b5cf6,stroke:#a78bfa,color:#fff
  style D fill:#10b981,stroke:#34d399,color:#fff
  • Clients call the runtime's REST endpoints
  • Flow runtime uses FlowEngine from @hazeljs/flow with in-memory or Prisma storage
  • Recovery runs on startup when using Prisma to continue any runs that were RUNNING when the process stopped

Installation

pnpm add @hazeljs/flow-runtime @hazeljs/flow

@hazeljs/flow is required; flow-runtime wraps it and exposes the HTTP API.

Running the runtime

As a standalone process

# From the flow-runtime package
pnpm dev
# or
pnpm start   # after build

Requires PORT (default 3000) and optionally DATABASE_URL for Postgres. If DATABASE_URL is missing or the connection fails, the runtime falls back to in-memory storage.

From your app (programmatic)

Invoke the runtime from your application so you register your own flows and services—no need to reimplement the HTTP API:

import { runFlowRuntime } from '@hazeljs/flow-runtime';
import { buildFlowDefinition } from '@hazeljs/flow';
import { OrderFlow } from './flows/OrderFlow';

await runFlowRuntime({
  port: 3000,
  databaseUrl: process.env.DATABASE_URL,  // optional; in-memory if omitted
  flows: [buildFlowDefinition(OrderFlow)],
  services: { logger: myLogger, slack: slackClient },
});

The server listens on port and runs recovery on startup when using Prisma storage.

API Reference

MethodPathDescription
POST/v1/runs/startStart a new flow run (body: flowId, version, input)
POST/v1/runs/:runId/tickAdvance a running run one step
POST/v1/runs/:runId/resumeResume a waiting run (body: payload for the wait)
GET/v1/runs/:runIdGet run status and current state
GET/v1/runs/:runId/timelineGet event timeline for the run
GET/v1/flowsList registered flow definitions
GET/healthHealth check

Start a run

curl -X POST http://localhost:3000/v1/runs/start \
  -H "Content-Type: application/json" \
  -d '{"flowId":"order-flow","version":"1.0.0","input":{"orderId":"ORD-123"}}'

Response includes runId. Use it to tick or fetch status.

Resume a waiting run

When a node returns { status: 'wait', reason: '...' }, the run stays in WAITING. To continue:

curl -X POST http://localhost:3000/v1/runs/:runId/resume \
  -H "Content-Type: application/json" \
  -d '{"approved":true}'

Get run status and timeline

curl http://localhost:3000/v1/runs/:runId
curl http://localhost:3000/v1/runs/:runId/timeline

Configuration

Env / optionDescription
PORTHTTP port (default 3000)
DATABASE_URLOptional Postgres URL; if unset or connection fails, uses in-memory storage

When using Postgres, run the flow package's Prisma migrations so the runtime can persist runs and events.

Recovery

On startup, the runtime calls engine.getRunningRunIds() and ticks each run (with concurrency limit). This continues any run that was RUNNING when the process last stopped, so you get crash recovery without manual scripts.

Example repo

The hazeljs-flow-example repo includes order-processing, approval, and fraud-detection flows and shows how to start the runtime with runFlowRuntime(...) and call the API from a client.

What's next?

  • Flow — Define workflows with decorators, persistence, wait/resume, and idempotency
  • Prisma — Optional persistence for flow-runtime uses the flow package's Prisma schema; use the same patterns for your app data