Local Development
Last reviewed: 2026-05-13
Maintained by: Engineering
This is the practical guide for day-to-day local work in the repo.
Canonical docs entrypoint: developer-index.md
First-Time Setup
- Activate
pnpmif it is not already available:
corepack prepare pnpm@9.15.9 --activate
- Install dependencies:
pnpm install
- Copy the env template:
cp .env.example .env
For app-local env files:
cp apps/manager-desk/.env.example apps/manager-desk/.env.local
cp apps/back-office/.env.example apps/back-office/.env.local
cp apps/zgrid-mobile/.env.example apps/zgrid-mobile/.env
Local email defaults:
- keep
EMAIL_PROVIDER=dev_login local development so no real emails are sent EMAIL_FROM,EMAIL_REPLY_TO, andEMAIL_SUPPORT_ADDRESSare local placeholders and must be replaced with real values outside local devEMAIL_ACTION_BASE_URLshould point at your local Manager Desk sign-in / action flow originMANAGER_DESK_URLshould point at your local Manager Desk app originRESEND_API_KEYis not required locally and should only be added in staging/production whenEMAIL_PROVIDER=resend
Local Sentry defaults:
- keep
SENTRY_ENABLED=falsein the root.env - keep
NEXT_PUBLIC_SENTRY_ENABLED=falseinapps/manager-desk/.env.local - keep
NEXT_PUBLIC_SENTRY_ENABLED=falseinapps/back-office/.env.local - keep
EXPO_PUBLIC_SENTRY_ENABLED=falseinapps/zgrid-mobile/.env - if you want to verify Sentry locally, set the matching DSN and turn on the matching enabled flag
- Start local Postgres:
docker compose up -d postgres
- Apply migrations:
pnpm run db:migrate
- Seed demo data:
pnpm run seed:demo
Important:
- the current schema includes
issue_kind, strict emergency categories, andservice_requeststables - if you pull latest
mainon an existing local DB, runpnpm run db:migratebefore using Issues/Service Requests screens
Running the Apps
Through Root Scripts
This is the standard team workflow:
pnpm run dev:api
pnpm run dev:manager-desk
Directly with pnpm --filter
Use this when you want to work in only one workspace:
pnpm --filter @zgrid/api dev
pnpm --filter @zgrid/manager-desk dev
Rule:
- use root scripts for the standard team workflow
- use
--filterfor focused work in a single app or package
Local URLs
- API:
http://localhost:3000 - Manager Desk:
http://localhost:3001 - Zgrid App mobile: Expo dev server via
pnpm run dev:zgrid-mobile
Zgrid App
The mobile app lives in apps/zgrid-mobile and uses Expo Router.
Common commands:
pnpm run dev:zgrid-mobile
pnpm run ios:zgrid-mobile
pnpm run android:zgrid-mobile
pnpm run check:zgrid-mobile
pnpm run lint:zgrid-mobile
pnpm run cleanup:privacy-metadata
The mobile app reads EXPO_PUBLIC_API_URL. If it is not set, it defaults to:
- iOS / simulator-friendly:
http://127.0.0.1:3000/api - Android emulator-friendly:
http://10.0.2.2:3000/api
Examples:
EXPO_PUBLIC_API_URL=http://127.0.0.1:3000/api pnpm run dev:zgrid-mobile
EXPO_PUBLIC_API_URL=http://10.0.2.2:3000/api pnpm run android:zgrid-mobile
If you want mobile Sentry on a non-local build, also provide:
EXPO_PUBLIC_SENTRY_ENABLED=true
EXPO_PUBLIC_SENTRY_DSN=https://examplePublicKey@o0.ingest.sentry.io/0
EXPO_PUBLIC_SENTRY_ENVIRONMENT=staging
EXPO_PUBLIC_SENTRY_RELEASE=mobile@2026.05.13
EXPO_PUBLIC_SENTRY_TRACES_SAMPLE_RATE=0.05
- Postgres:
localhost:5433
Migrations
Create a New Migration
pnpm run db:migrate:create add-some-change
Apply Pending Migrations
pnpm run db:migrate
Roll Back the Latest Migration
pnpm run db:migrate:down
Rules:
- do not edit old migrations that have already been applied
- every new schema change should go into a new migration
database/init.sqlis not the source of truth for ongoing schema changes
Checks Before Commit
pnpm run lint
pnpm test
pnpm run check:api
pnpm run check:manager-desk
If you changed the database:
pnpm run db:migrate
pnpm run db:schema:check
pnpm run seed:demo
db:schema:check prerequisites:
- PostgreSQL client tools installed with
pg_dumpavailable in yourPATH - Postgres running and reachable via current
DATABASE_URL(default local setup useslocalhost:5433)
If you intentionally changed the schema and added a new migration:
pnpm run db:migrate
pnpm run db:schema:sync
Common Problems
| Symptom | Most common cause | Command / fix |
|---|---|---|
| API query fails | database is not migrated | pnpm run db:migrate |
| Seed fails | schema and seed are out of sync | pnpm run db:migrate then pnpm run seed:demo |
| Migration drift check fails | schema snapshot is stale | pnpm run db:migrate then pnpm run db:schema:sync |
| Lint fails | ESLint rule or unused import | pnpm run lint and fix reported files |
| Tests fail | a utility or env contract changed | pnpm test and fix the failing workspace |
| API typecheck fails | backend TypeScript error | pnpm run check:api |
| Manager Desk typecheck fails | Next.js or route typing error | pnpm run check:manager-desk |
| Web shows stale build state | stale .next artifacts | remove apps/manager-desk/.next and restart dev |
| Next type info looks stale | stale TS build info | remove apps/manager-desk/tsconfig.tsbuildinfo |
| Login breaks after auth changes | stale cookie or seed state | log out, log back in, and if needed run pnpm run seed:demo |
When to Use Root Scripts vs Filters
Use root scripts when:
- you want the standard team command
- you are running check, lint, migrate, or seed commands
- you are documenting commands in README or onboarding docs
Use pnpm --filter when:
- you are adding a dependency to one workspace only
- you want dev or build for a single workspace
- you are doing targeted debugging
Source of Truth for Local Setup
When you change the local workflow, keep these aligned:
- docs home
- local-development.md
package.json.env.example