Skip to content
Back to work
Case study2025~5 weeksSole developer · workflow engine + UI

Flow Craft

A visual workflow builder where AI is a first-class node. Drag, connect, and trigger pipelines that summarize, classify, and generate — backed by a real queue, not a for-loop.

3node types · trigger/action/AI
5+AI tasks supported
fan-out via queue retries
1click → external webhook trigger

01Problem

AI workflows usually live in scripts no one reads.

Most teams I've worked with end up with a folder of Python scripts that call an LLM, maybe transform the output, post to a Slack webhook, and pray. They run on cron, fail silently, and nobody can change them without becoming an honorary infra engineer.

FlowCraft turns those scripts into editable, observable workflows. A non-engineer can wire a trigger → AI summarize → POST to webhook in three nodes. An engineer can drop in a custom action when needed. Both see the same pipeline, the same logs, and the same failure modes.

02Architecture

Web tier and execution tier, decoupled by a queue.

Next.js handles the UI, the editor, and the workflow definitions. A standalone TypeScript worker (its own container) pulls jobs from Redis via BullMQ and writes results back to Postgres. Both tiers share Prisma and the workflow type definitions.

Web appNext.js · React FlowAPI routesauth · workflow CRUDWebhook ingressexternal triggersRedis · BullMQjob queuePostgreSQLPrismaWorker containerstandalone TS process · pulls jobs · runs nodes · writes resultsSSRPOSTenqueuedequeuewriteback
Next.jsTypeScriptReact FlowPrismaPostgreSQLBullMQRedisOpenAIGroq

03Workflow engine

Three node families, one execution model.

Drag-and-drop builder

Trigger nodes (webhook, schedule, manual), action nodes (HTTP, DB, transform), and AI nodes (summarize, classify, generate). Connect them on an infinite canvas.

Native AI nodes

AI is a first-class node type, not a wrapper around an HTTP call. Provider-agnostic — swap OpenAI for Groq without touching the workflow.

Async execution

BullMQ-backed workers run workflows out of process. Retries with backoff, dead-letter queues for permanent failures, and per-workspace rate limiting.

External triggers

Every workflow gets a webhook URL and a REST endpoint. Wire it to GitHub, Slack, or any service that can POST.

04Decisions

Decisions worth keeping a paper trail on.

Why a queue (BullMQ) instead of inline execution

Long-running AI calls can't block the request thread. BullMQ on Redis gives me durable jobs, retries with exponential backoff, fan-out across workers, and a clean separation between the web tier (Next.js) and the execution tier (a standalone TS worker). The web app stays snappy even when a workflow runs for 30 seconds.

Why a standalone worker process

Next.js serverless functions have wall-clock and memory limits unfit for orchestration. The worker runs in its own container (Render / Fly), pulls jobs off Redis, and persists state to Postgres via Prisma. Web and worker scale independently.

Why React Flow for the editor

Building a node-graph editor from scratch is a several-month rabbit hole. React Flow handles the canvas, minimap, edge routing, and pan/zoom. The workflow definition is its JSON, which I persist directly — no translation layer.

Why multiple AI providers (OpenAI + Groq + mock)

Lock-in to one model means lock-in to one bill. The AI node is a small abstraction that routes to OpenAI for accuracy or Groq for speed. The 'mock' provider returns canned output for local development so I don't burn credits debugging UI.

05Tradeoffs

Where it got sharp.

  • 01

    Idempotency for re-tried jobs

    Workers retry on transient failures, which means the same node might execute multiple times. Each run logs a deterministic execution ID, and downstream nodes check for it before re-executing. Side-effecting nodes (webhooks, emails) gate on this ID.

  • 02

    Visual editor ↔ execution state

    Users want to see their workflow light up as it runs. Solved with a polling endpoint (could be SSE later) that returns per-node status. React Flow's edge animations turn on for the currently-active path.

  • 03

    Tenant isolation at the queue level

    Multiple workspaces share the same worker pool. Jobs carry their workspaceId; the worker enforces row-level access on every Prisma query. Webhooks include a workspace-scoped signing secret so trigger payloads can't cross-pollinate.

Next

Want to talk through how it's built?

Happy to walk through the code, the deployment, or any of these decisions in more depth.