Skip to content

Local development

This page gets QA Sync running locally with a full Supabase stack (Postgres, Auth, Storage, Studio) in Docker.

  • Docker Desktop, running
  • Supabase CLI
  • Node.js and npm: use a Node binary whose architecture matches your machine (for example arm64 on Apple silicon); see Rollup native module

QA Sync uses npm. Don’t add another package manager’s lockfile.

Set in supabase/config.toml:

Service Port
App (Vite) 3000
Supabase API 58321
Postgres 58322
Supabase Studio 58323
Email inbox (Inbucket) 58324
  1. Install dependencies:

    Terminal window
    npm install
  2. Start local Supabase and apply migrations and seed data:

    Terminal window
    supabase start
    supabase db reset
  3. Get the local anon key:

    Terminal window
    supabase status
  4. Create .env.local in the repository root with the browser-safe values:

    VITE_SUPABASE_URL="http://127.0.0.1:58321"
    VITE_SUPABASE_ANON_KEY="<anon key from supabase status>"
  5. Start the app:

    Terminal window
    npm run dev
  6. Open http://localhost:3000/app/ and sign in with a seeded account.

npm run dev runs scripts/dev.mjs from the repository root, which:

  1. starts local Supabase (Docker must be running), restarting it once if it doesn’t come up cleanly;
  2. prints the local links: web app, landing page, docs, get-started form, Studio (http://127.0.0.1:58323), email inbox, API and Postgres;
  3. runs the web app (apps/web) on port 3000 and the landing + docs site (apps/landing) on port 4321;
  4. on Ctrl+C, stops both apps and then runs supabase stop. Your local data is kept.

It refuses to start if port 3000 or 4321 is already in use and tells you which process holds it, and if Node is older than 22.23.4 (run nvm use in the repository root).

To run one app without Supabase:

Terminal window
npm run dev:web # apps/web on port 3000
npm run dev:landing # apps/landing on port 4321
File Purpose Committed?
.env.local Local browser-safe Supabase URL and anon key. No
.env.development.local Optional dev-only login shortcuts. No
.env.production Only for validating a release build locally. No

All env files are git-ignored. Anything in a VITE_ variable is bundled into the browser, so never put a service-role key in a Vite env file.

To get one-click login buttons on the local login screen, add VITE_DEV_LOGIN_SHORTCUTS to .env.development.local:

VITE_DEV_LOGIN_SHORTCUTS="Admin|admin@qasync.com|Admin@001;Developer|mrval@qasync.com|Dev@0001;Tester|debbie@qasync.com|Tester@001"
Role Email Password
Admin admin@qasync.com Admin@001
Developer mrval@qasync.com Dev@0001
Developer uchenna@qasync.com Dev@0002
Tester debbie@qasync.com Tester@001
Tester tina@qasync.com Tester@002

The seed keeps auth.users, auth.identities, public.access_directory and public.profiles aligned. Seeded testers and developers have access to the app_qasync app, and seeded tester groups belong to it. The seed also includes sample milestones and sprints.

Command What it does
npm run dev Supabase + web app + landing/docs site; stops Supabase on exit.
npm run dev:web Web app only, port 3000.
npm run dev:landing Landing + docs site only, port 4321.
npm run lint tsc --noEmit type-check of the web app. Run before you say you’re done.
npm run build Builds both apps and assembles the deployable site in dist/: landing at /, docs at /docs/, app at /app/.
npm run preview Builds, then serves dist/ on port 4173 with the same redirects and rewrites as Vercel.
npm run clean Deletes both apps’ dist/.
supabase db reset Wipes and rebuilds the local database with migrations and seed.
supabase db push --local Applies new migrations to the local database.
supabase status Shows local services, URLs and keys.

See Validation scripts for validate:local-env, validate:core and validate:prod-env.

During npm run dev, Vite Fast Refresh may remount changed modules. QA Sync keeps the last resolved auth state in session storage, so most saves show a small in-shell “refreshing workspace” notice instead of the full sign-in splash. Env, config and dependency changes can still force a full reload.

The app code is ahead of your local schema. Apply migrations; don’t patch tables by hand:

Terminal window
supabase db reset
  1. Confirm all migrations are applied (supabase db reset).
  2. Confirm the user exists in both auth.users and public.profiles.
  3. Confirm .env.local points at the same Supabase instance as your CLI.

Infinite recursion in a public.profiles policy. It’s fixed by migration 20260407113000_fix_profiles_policy_recursion.sql; apply migrations with supabase db push --local.

Vite fails with a missing Rollup native module

Section titled “Vite fails with a missing Rollup native module”
Cannot find module @rollup/rollup-darwin-x64

Your Node architecture doesn’t match the installed optional Rollup package. Reinstall with the same Node binary you use to run Vite:

Terminal window
npm install

Auth and profile records must stay aligned. Prefer supabase db reset over manual inserts into public.profiles.

The repository’s .gitignore ignores folders named Debug, Release, x64, x86, obj, out, log, logs, bin, artifacts and build at any depth. Folders under apps/web/src/ are re-included, but elsewhere (for example in scripts/ or supabase/) run git check-ignore -v <folder> before committing a new folder.