Files
platform/apps/api

@blade-and-brawn/api

ElysiaJS backend for Blade & Brawn: the level calculator endpoint, Printful/Webflow commerce sync, and the admin API used by apps/portal.

Stack

  • ElysiaJS on Bun, run in node:cluster worker processes (see src/index.ts; worker count is min(CPU cores, MAX_WORKER_COUNT))
  • PostgreSQL via Kysely
  • JWT cookie auth (@elysia/jwt)

Environment variables

All required unless noted. See src/util.ts for the source of truth.

Variable Purpose
PRINTFUL_AUTH Printful API token
PRINTFUL_STORE_ID Printful store ID
PRINTFUL_WEBHOOK_SECRET Shared secret appended to the Printful webhook URL
WEBFLOW_SITE_ID Webflow site ID
WEBFLOW_COLLECTION_PRODUCTS_ID Webflow CMS collection ID for products
WEBFLOW_COLLECTION_SKUS_ID Webflow CMS collection ID for SKUs
WEBFLOW_AUTH Webflow API token
WEBFLOW_WEBHOOK_SECRET Webflow webhook signing secret
AUTH_SECRET JWT signing secret (must match AUTH_SECRET in apps/portal)
ADMIN_PASSWORD sha256 hex digest checked against on /auth/login
DATABASE_URL Postgres connection string
DATABASE_POOL_MAX Postgres pool size
MAX_WORKER_COUNT Upper bound on cluster worker processes
NODE_ENV optional, defaults to development
LOG_LEVEL optional, defaults to info

Scripts

Run from the repo root as bun run dev:api / bun run build:api, or from this directory:

  • dev — run with hot reload
  • start — run without hot reload
  • build — compile to a standalone binary at dist/server
  • db:migrate:latest / :rollback / :up / :down / :reset — Kysely migrations (src/database/migrations)
  • db:types:gen — regenerate src/database/out/db.d.ts from the database schema
  • db:seed — run src/database/seed.ts (loads seed-data/standards-config.json)

Structure

  • src/server.ts — Elysia app: routes, plugins, error handling
  • src/services/ — calculator, standards, commerce, events, event queue
  • src/database/ — Kysely instance, migrations, seed data
  • src/scripts/ — one-off scripts (e.g. register-webhooks.ts to (re)register Printful/Webflow webhooks against this API's domain)

Deployment

Deployed to Fly.io (fly.toml). After a domain changes, re-run src/scripts/register-webhooks.ts with production env vars to point Printful's and Webflow's webhooks at the new domain — see issue #3 for the full checklist.

Running migrations/seed against production

The deployed image only contains the compiled binary (see Dockerfile) — no source, no bun_modules, no migration files — so these can't be run from fly ssh console on the API app itself. Instead, use migrate.sh at the repo root, which tunnels to the Postgres app (blade-and-brawn-db, legacy/unmanaged Fly Postgres) via fly proxy, fetches the production DATABASE_URL for you, and runs the scripts against it:

./migrate.sh migrate   # db:migrate:latest (default if no argument given)
./migrate.sh seed      # db:seed
./migrate.sh both      # both, in order

Both underlying scripts prompt for a y/N confirmation before touching the database, and db:seed is idempotent (skips seeding if the default rows already exist). If the tunneled connection fails on TLS, legacy Postgres sometimes needs ?sslmode=disable appended — edit migrate.sh if so.