Local development
This page gets QA Sync running locally with a full Supabase stack (Postgres, Auth, Storage, Studio) in Docker.
Prerequisites
Section titled “Prerequisites”- 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.
Local ports
Section titled “Local ports”Set in supabase/config.toml:
| Service | Port |
|---|---|
| App (Vite) | 3000 |
| Supabase API | 58321 |
| Postgres | 58322 |
| Supabase Studio | 58323 |
| Email inbox (Inbucket) | 58324 |
First run
Section titled “First run”-
Install dependencies:
Terminal window npm install -
Start local Supabase and apply migrations and seed data:
Terminal window supabase startsupabase db reset -
Get the local anon key:
Terminal window supabase status -
Create
.env.localin 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>" -
Start the app:
Terminal window npm run dev -
Open http://localhost:3000/app/ and sign in with a seeded account.
npm run dev
Section titled “npm run dev”npm run dev runs scripts/dev.mjs from the repository root, which:
- starts local Supabase (Docker must be running), restarting it once if it doesn’t come up cleanly;
- prints the local links: web app, landing page, docs, get-started form, Studio (
http://127.0.0.1:58323), email inbox, API and Postgres; - runs the web app (
apps/web) on port3000and the landing + docs site (apps/landing) on port4321; - 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:
npm run dev:web # apps/web on port 3000npm run dev:landing # apps/landing on port 4321Environment files
Section titled “Environment files”| 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.
Quick dev logins
Section titled “Quick dev logins”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"Seeded local accounts
Section titled “Seeded local accounts”| Role | 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.
Everyday commands
Section titled “Everyday commands”| 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.
Fast Refresh
Section titled “Fast Refresh”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.
Troubleshooting
Section titled “Troubleshooting”column apps.current_status does not exist
Section titled “column apps.current_status does not exist”The app code is ahead of your local schema. Apply migrations; don’t patch tables by hand:
supabase db resetAuthenticated, but sent to Access Pending
Section titled “Authenticated, but sent to Access Pending”- Confirm all migrations are applied (
supabase db reset). - Confirm the user exists in both
auth.usersandpublic.profiles. - Confirm
.env.localpoints at the same Supabase instance as your CLI.
Profile lookup fails with 42P17
Section titled “Profile lookup fails with 42P17”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-x64Your Node architecture doesn’t match the installed optional Rollup package. Reinstall with the same Node binary you use to run Vite:
npm installData looks out of sync
Section titled “Data looks out of sync”Auth and profile records must stay aligned. Prefer supabase db reset over manual inserts into public.profiles.
New folder not committed
Section titled “New folder not committed”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.