Beauty BoothDocs

Development

Day-to-day commands, the demo store, and the live API check.

Commands

CommandDoes
pnpm run devAll apps via Turbo
pnpm run dev:web · dev:admin · dev:apiOne app on its own port
pnpm run type-checktsc --noEmit in every app — or type-check:<app> for one
pnpm run lintESLint in every app (no-explicit-any is an error) — or lint:<app>
pnpm run buildProduction builds for every app — or build:<app> (Turbo builds its dependencies first)
pnpm run dev:worker · start:workerThe background worker — email, Stripe webhook, refund and scheduled jobs from the Postgres queue (pg-boss). Needs JOBS_MODE=queue; without it the API runs those jobs inline and the worker is not needed
pnpm run seedLoad the demo catalogue into beauty-booth — same as pnpm --filter api run seed:bloom-booth (-- --fresh rebuilds the catalogue)
pnpm --filter api run assets:uploadPut the demo photos on ImageKit under bloom-booth/ — idempotent, run before the first seed
pnpm run testUnit and component tests — Jest in the API, Vitest with jsdom and Testing Library in the storefront and the dashboard
pnpm run test:e2ePlaywright smoke test: its own API, storefront and dashboard against the bloom_booth_e2e database, one cash order through every job (test:e2e:ui for the UI, test:e2e:report for the last report)
pnpm run check:apiLive check suite against a running API — creates and removes its own records
pnpm --filter api run create:admin <email> <password> [name]Headless administrator

The demo store

apps/api/data/bloom-booth/ holds the demo beauty catalogue as data: content.json — 67 products, 8 aisles and 31 sections, 41 brands, 6 journal posts, 3 hero slides, testimonials, sale promos and coupons — and image-manifest.json, the ImageKit URL of every photo. assets:upload downloads each product photo once and uploads it under bloom-booth/; seed:bloom-booth writes the documents with those URLs. Both are idempotent — documents are upserted on their slug and finished uploads are remembered, so a second run changes nothing.

bash
pnpm --filter api run assets:upload           # photos → ImageKit, under bloom-booth/
pnpm --filter api run seed:bloom-booth         # load or refresh the demo catalogue
pnpm --filter api run seed:bloom-booth -- --fresh   # empty the catalogue and CMS collections first
Scripts are scoped on purposeThe scripts connect through scripts/lib/dbGuard.ts: they refuse any database but beauty-booth (or BLOOM_DB_NAME) before a single query runs, and the asset scripts refuse any media root but bloom-booth/. If the cluster or the ImageKit account is shared with another store, never loosen either check — --fresh and assets:purge delete.

It never creates customers or orders and never touches users, orders or your Configuration.

The live API check

check:api exercises a *running* server end to end — middleware order, auth chains, response envelopes, role rules — which unit tests against controllers cannot. It creates its own admin and customer, and its teardown removes every record it made, including leftovers from a crashed run.

check:api
pnpm run check:api                                    # against http://localhost:8000
API_URL=https://api-staging.example.com pnpm run check:api

Next.js gotchas already handled

  • <html> carries data-scroll-behavior="smooth" — Next 16 no longer overrides smooth scrolling during navigation without it, and you land mid-page.
  • Never call setState inside an effect to react to a route change; adjust during render with a previous-value guard. ESLint enforces it.
  • Per-user reads use cache: "no-store" — Next's Data Cache keys on the URL, not the session.
  • A client component must not import a *value* from a module that reaches next/headers; tsc will not catch it, only pnpm run build does.
  • A page reads what it shows, once. Shared storefront readers are wrapped in React cache() and take a limit; the dashboard counts with count queries (/api/contacts/count, /api/stats) and reads grids through view=table — never fetch a list to measure it or sort 500 rows in JS.
Buy Beauty BoothFull source code$99.99