A browser-based visual AI workflow builder. Compose multi-step LLM pipelines on a React Flow canvas, execute them reliably via Inngest's durable job engine, and manage encrypted AI credentials — all in a single Next.js 15 application.
- Overview
- Architecture
- Tech Stack
- Prerequisites
- Quick Start — Docker
- Quick Start — Local Dev
- Environment Variables
- Database
- External Services Setup
- Development Scripts
- Production Deployment
- Project Structure
Nodebase lets users build directed-acyclic-graph (DAG) pipelines visually and execute them against real LLM providers. A workflow consists of trigger nodes (what starts execution), AI nodes (what does the work), and action nodes (what happens with the output).
Supported node types
| Category | Nodes |
|---|---|
| Triggers | Manual, HTTP Request, Google Form, Stripe Webhook |
| AI Models | Anthropic Claude, OpenAI GPT, Google Gemini |
| Actions | Discord, Slack |
Key design decisions:
- Inngest handles all async execution — retries, fan-out, and realtime status updates are free without managing queues.
- Better Auth owns the session layer so the app is not coupled to a specific cloud provider's auth.
- Cryptr (AES-256-GCM) encrypts API credentials at rest in Postgres — the encryption key never leaves the server.
- Polar manages subscription billing without building a checkout flow.
┌───────────────────────────────────────────────────┐
│ Browser │
│ React 19 · XYFlow (canvas) · Jotai · tRPC hooks │
└────────────────────┬──────────────────────────────┘
│ HTTPS (tRPC / RSC)
┌────────────────────▼──────────────────────────────┐
│ Next.js 15 (App Router) │
│ │
│ /api/trpc/[trpc] — tRPC procedure router │
│ /api/auth/[...all] — Better Auth handler │
│ /api/inngest — Inngest event receiver │
│ /api/webhooks/* — HTTP / Stripe / Google Forms │
└───────┬───────────────────────┬────────────────────┘
│ │
┌────────────────▼───┐ ┌──────────▼──────────────┐
│ PostgreSQL 16 │ │ Inngest Cloud / Dev │
│ (via Prisma 6) │ │ executeWorkflow fn │
│ │ │ topological sort │
│ user │ │ per-node executor │
│ session │ └─────────────────────────┘
│ account │
│ workflow │ ┌─────────────────────────┐
│ node │ │ External Providers │
│ connection │ │ Anthropic / OpenAI / │
│ credential │ │ Gemini / Discord / │
│ execution │ │ Slack / Stripe / etc. │
└───────────────────┘ └─────────────────────────┘
Execution flow:
- User saves a workflow (nodes + connections persisted to Postgres via tRPC).
- A trigger fires — manual button, incoming HTTP POST, form submission, or Stripe event.
- The trigger handler sends a
workflows/execute.workflowevent to Inngest. executeWorkflow(insrc/inngest/functions.ts) fetches the graph, topologically sorts nodes, and runs each executor in order, threading output context between steps.- Execution status (
RUNNING → SUCCESS | FAILED) is written back to Postgres in real-time.
| Layer | Technology |
|---|---|
| Framework | Next.js 15.5 (App Router, Turbopack) |
| UI | React 19, Tailwind CSS 4, shadcn/ui (Radix), XYFlow |
| API | tRPC 11 + TanStack Query 5 |
| Auth | Better Auth 1.3 (GitHub & Google OAuth, email/password) |
| ORM | Prisma 6 |
| Database | PostgreSQL 16 |
| Async Jobs | Inngest 3 |
| AI SDK | Vercel AI SDK 5 |
| AI Providers | Anthropic, OpenAI, Google Gemini |
| Billing | Polar |
| Encryption | Cryptr (AES-256-GCM) |
| State | Jotai (editor atoms), nuqs (URL state) |
| Monitoring | Sentry |
| Code Quality | Biome 2 |
| Tool | Min version | Notes |
|---|---|---|
| Node.js | 20.x | LTS; check with node -v |
| npm | 10.x | Bundled with Node 20 |
| Docker | 24.x | For the containerised path |
| Docker Compose | v2.x | docker compose version |
This path runs the full stack (Postgres + app + auto-migration) with a single command.
# 1. Clone
git clone <repo-url> nodebase && cd nodebase
# 2. Configure environment
cp .env.example .env
# Edit .env — at minimum set:
# POSTGRES_PASSWORD, BETTER_AUTH_SECRET, ENCRYPTION_KEY
# 3. Build & start
docker compose up --build
# 4. Open http://localhost:3000Postgres data is persisted in the postgres_data Docker volume. The container runs prisma migrate deploy before starting Next.js, so the schema is always current.
Tear down (keeps data)
docker compose downTear down (destroy data)
docker compose down -vUse this path for active development — hot reload, Prisma Studio, Inngest dev UI.
# 1. Install dependencies
npm install
# 2. Configure environment
cp .env.example .env.local
# Set DATABASE_URL to your local Postgres instance
# 3. Generate Prisma client & push schema
npx prisma generate
npx prisma migrate dev
# 4. Start everything (Next.js + Inngest dev server + ngrok)
npm run dev:all
# — or start services individually:
npm run dev # Next.js on :3000
npm run inngest:dev # Inngest dev UI on :8288
npm run ngrok:dev # ngrok tunnel (requires NGROK_URL in .env.local)Inngest dev server: Inngest must be running locally for workflow execution to work in development. The dev UI at
http://localhost:8288lets you inspect, replay, and debug function runs.
Copy .env.example to .env (Docker) or .env.local (local dev) and fill in the required values.
| Variable | Description |
|---|---|
DATABASE_URL |
PostgreSQL connection string. Docker Compose constructs this automatically; set it manually for local dev. |
BETTER_AUTH_SECRET |
32+ character random secret for signing sessions. Generate: openssl rand -base64 32 |
BETTER_AUTH_URL |
Public base URL of the app, no trailing slash (e.g. https://app.example.com). |
ENCRYPTION_KEY |
Hex string used to AES-encrypt stored AI credentials. Changing this after first use renders all saved credentials unreadable. Generate: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))" |
POSTGRES_PASSWORD |
Password for the db Compose service. Not needed for local dev. |
Required if you want GitHub / Google login. Email + password auth works without these.
| Variable | Where to get it |
|---|---|
GITHUB_CLIENT_ID |
github.com → Settings → Developer settings → OAuth Apps |
GITHUB_CLIENT_SECRET |
Same as above |
GOOGLE_CLIENT_ID |
console.cloud.google.com → APIs & Services → Credentials |
GOOGLE_CLIENT_SECRET |
Same as above |
Callback URL pattern: {BETTER_AUTH_URL}/api/auth/callback/{provider}
| Variable | Description |
|---|---|
INNGEST_EVENT_KEY |
Production only — from app.inngest.com. Leave blank for local dev (auto-discovered). |
INNGEST_SIGNING_KEY |
Production only — used to verify webhook payloads from Inngest Cloud. |
| Variable | Description |
|---|---|
POLAR_ACCESS_TOKEN |
Personal access token from polar.sh / sandbox.polar.sh |
POLAR_SUCCESS_URL |
Redirect URL after successful checkout (e.g. https://app.example.com/workflows) |
| Variable | Description |
|---|---|
SENTRY_DSN |
Ingest URL for error reporting |
SENTRY_AUTH_TOKEN |
Build-time token for uploading source maps to Sentry |
NGROK_URL |
Static ngrok domain for webhook testing in local dev |
POSTGRES_USER |
Postgres username (default: nodebase) |
POSTGRES_DB |
Postgres database name (default: nodebase) |
DB_PORT |
Host port for the db container (default: 5432) |
APP_PORT |
Host port for the app container (default: 3000) |
The app uses Prisma migrations. All migration files live in prisma/migrations/.
# Apply all pending migrations (safe for production)
npx prisma migrate deploy
# Create a new migration during development
npx prisma migrate dev --name <descriptive-name>
# Open Prisma Studio (browser-based DB browser)
npx prisma studio
# Re-generate the Prisma client after schema changes
npx prisma generateUser ──< Session
──< Account (OAuth connections)
──< Credential (encrypted API keys: OPENAI | ANTHROPIC | GEMINI)
──< Workflow ──< Node ──< Connection
└─< Execution
Credentials are encrypted with ENCRYPTION_KEY before insertion and decrypted only at execution time inside server-side Inngest functions — they are never sent to the browser.
Nodebase uses Inngest for durable, retryable workflow execution.
- Development: Run
npm run inngest:dev. The SDK auto-registerssrc/inngest/functions.tsagainsthttp://localhost:3000/api/inngest. - Production: Create an app at app.inngest.com, copy
INNGEST_EVENT_KEYandINNGEST_SIGNING_KEYinto your environment, and point the Inngest dashboard at your/api/inngestendpoint.
Used for subscription billing. The pro product ID is hardcoded in src/lib/auth.ts. If you fork this project, update that product ID to your own.
- Sandbox: sandbox.polar.sh
- Production: polar.sh
The app currently targets the Polar sandbox (server: "sandbox" in src/lib/polar.ts). Switch to "production" before going live.
Set the Authorization callback URL in each provider's OAuth app settings:
https://<your-domain>/api/auth/callback/github
https://<your-domain>/api/auth/callback/google
For local dev using ngrok, use:
https://<your-ngrok-domain>/api/auth/callback/github
https://<your-ngrok-domain>/api/auth/callback/google
| Script | What it does |
|---|---|
npm run dev |
Next.js dev server on :3000 with Turbopack |
npm run dev:all |
Parallel: Next.js + Inngest dev server + ngrok tunnel |
npm run inngest:dev |
Inngest dev server + UI on :8288 |
npm run ngrok:dev |
ngrok HTTP tunnel to :3000 using NGROK_URL from env |
npm run build |
Production build with Turbopack |
npm start |
Start production server (run build first) |
npm run lint |
Biome static analysis |
npm run format |
Biome auto-format (writes files) |
See Quick Start — Docker. For a production host:
- Point your DNS A record at the server.
- Terminate TLS at a reverse proxy (nginx, Caddy, Traefik) and proxy to port 3000.
- Set
BETTER_AUTH_URLto your public HTTPS domain. - Rotate
BETTER_AUTH_SECRETandENCRYPTION_KEYbefore go-live — these cannot be changed after users have signed up or saved credentials without a coordinated migration. - Configure
INNGEST_EVENT_KEYandINNGEST_SIGNING_KEYfrom Inngest Cloud. - Switch
src/lib/polar.tsfromserver: "sandbox"toserver: "production".
Recommended Caddy snippet (automatic TLS + HTTP/2):
app.example.com {
reverse_proxy app:3000
}
The project ships with Sentry's Vercel integration pre-configured. Remove output: "standalone" from next.config.ts before deploying to Vercel — the standalone output is only needed for self-hosted Docker.
Set all environment variables listed above in the Vercel dashboard under Settings → Environment Variables.
docker build -t nodebase:latest .
docker run --rm \
--env-file .env \
-e DATABASE_URL="postgresql://user:pass@host:5432/db" \
-p 3000:3000 \
nodebase:latestnodebase/
├── prisma/
│ ├── schema.prisma # Data model + generator config
│ └── migrations/ # Timestamped migration files
│
├── public/
│ └── logos/ # Node-type SVG logos
│
├── src/
│ ├── app/ # Next.js App Router
│ │ ├── (auth)/ # Login & signup pages
│ │ ├── (dashboard)/
│ │ │ ├── (editor)/ # Workflow canvas editor
│ │ │ └── (rest)/ # Credentials, executions, workflows list
│ │ └── api/
│ │ ├── auth/ # Better Auth catch-all
│ │ ├── inngest/ # Inngest event receiver
│ │ ├── trpc/ # tRPC handler
│ │ └── webhooks/ # HTTP / Stripe / Google Forms
│ │
│ ├── components/
│ │ ├── ui/ # shadcn/ui primitives
│ │ └── react-flow/ # XYFlow custom nodes & edges
│ │
│ ├── config/
│ │ ├── constants.ts # Pagination defaults
│ │ └── node-components.ts # Node type → component mapping
│ │
│ ├── features/ # Vertical feature slices
│ │ ├── auth/
│ │ ├── credentials/
│ │ ├── editor/ # Jotai atoms + editor components
│ │ ├── executions/ # Execution history + node executors
│ │ ├── triggers/
│ │ ├── workflows/
│ │ └── subscriptions/
│ │
│ ├── inngest/
│ │ ├── client.ts # Inngest instance
│ │ ├── functions.ts # executeWorkflow — topological executor
│ │ ├── utils.ts # Toposort helpers
│ │ └── channels/ # One executor per node type
│ │
│ ├── lib/
│ │ ├── auth.ts # Better Auth config
│ │ ├── db.ts # Prisma singleton
│ │ ├── encryption.ts # encrypt / decrypt (Cryptr)
│ │ └── polar.ts # Polar client
│ │
│ └── trpc/
│ ├── init.ts # Context + protectedProcedure middleware
│ ├── client.tsx # TRPCReactProvider
│ ├── server.tsx # Server caller + prefetch helpers
│ └── routers/ # Router tree (_app.ts + sub-routers)
│
├── .env.example # All supported environment variables
├── .dockerignore
├── docker-compose.yml # Postgres + app (production)
├── docker-entrypoint.sh # migrate → start
├── Dockerfile # Multi-stage production image
├── biome.json # Linting & formatting config
├── components.json # shadcn/ui config
├── mprocs.yaml # Dev: run next + inngest + ngrok in parallel
├── next.config.ts
├── prisma/schema.prisma
└── tsconfig.json