LOCAL_DEV_SETUP — staff-service
Sibling: SERVICE_OVERVIEW · DATA_MODEL · TESTING_STRATEGY
Strategic anchors: standards/SERVICE_TEMPLATE · 02 §17 Local Dev
How to bring staff-service up on a developer machine in under 5 minutes, run tests, and reproduce production-like behavior. Production runs on GCP (Cloud Run + Cloud SQL); locally we use Docker Compose with Postgres 16, Redis 7, and the Pub/Sub emulator.
1. Prerequisites
| Tool | Version | Purpose |
|---|---|---|
| Node.js | 20.x LTS | Runtime |
| pnpm | 9.x | Monorepo package manager |
| Docker Desktop | latest | Compose stack |
| gcloud CLI | latest | Pub/Sub emulator install (gcloud components install pubsub-emulator) |
| openssl | 3.x | Local CA / KMS pepper material |
The repo's .tool-versions is mise-compatible; mise install from the repo root handles Node + pnpm.
2. First-Time Setup
# from monorepo root
pnpm install
pnpm -F staff-service build
# create the local KMS pepper material
mkdir -p .secrets
openssl rand -hex 32 > .secrets/staff_pin_pepper.local
openssl rand -hex 32 > .secrets/staff_pii_envelope.local
# bring up dependencies (Postgres + Redis + Pub/Sub emulator)
pnpm -F staff-service dev:deps:up
# run migrations
pnpm -F staff-service db:migrate
# seed local fixtures
pnpm -F staff-service db:seed
dev:deps:up calls docker compose -f docker-compose.dev.yml up -d from services/staff-service/. The compose file is committed and pinned to specific image tags.
3. Running the Service
# all entrypoints, with hot-reload
pnpm -F staff-service dev
# or specific entrypoints
pnpm -F staff-service dev:api
pnpm -F staff-service dev:worker
pnpm -F staff-service dev:cron
dev runs all three entrypoints in parallel under concurrently. Logs are colored per process. Default ports: API on :3070, OpenAPI explorer on :3070/docs.
4. Environment Variables
A .env.local is generated by pnpm -F staff-service env:bootstrap based on .env.example. Key entries:
NODE_ENV=development
PORT=3070
LOG_LEVEL=debug
DATABASE_URL=postgres://staff:staff@localhost:55432/staff
REDIS_URL=redis://:devpass@localhost:56379/0
PUBSUB_PROJECT_ID=local-dev
PUBSUB_EMULATOR_HOST=localhost:58085
IAM_SERVICE_BASE_URL=http://localhost:3050
PROPERTY_SERVICE_BASE_URL=http://localhost:3030
AI_ORCHESTRATOR_BASE_URL=http://localhost:3090
KMS_PIN_PEPPER_KEY=file://./.secrets/staff_pin_pepper.local
KMS_PII_ENVELOPE_KEY=file://./.secrets/staff_pii_envelope.local
STAFF_AUTO_CLOSE_GRACE_MIN=60
STAFF_GAP_WARN_MIN=15
STAFF_PIN_LOCKOUT_MIN=15
STAFF_PIN_PEPPER_VERSION=v3
The file:// KMS scheme is a local-only adapter that reads from disk; CI rejects any file:// outside NODE_ENV=test.
5. Compose Services
| Container | Image | Host port | Notes |
|---|---|---|---|
staff-postgres | postgres:16.4-alpine | 55432:5432 | Persistent volume staff_pg_data |
staff-redis | redis:7.4-alpine | 56379:6379 | AUTH devpass |
pubsub-emulator | gcr.io/google.com/cloudsdktool/cloud-sdk:emulators | 58085:8085 | Auto-creates topics + subs from pubsub.dev.yaml |
mailpit | axllent/mailpit:v1 | 58025:8025 (UI), 51025:1025 (SMTP) | Captures invite emails (via iam-service) |
Bring down with pnpm -F staff-service dev:deps:down. Reset state with dev:deps:nuke (drops volumes).
6. Seed Data
pnpm -F staff-service db:seed populates:
- 1 tenant:
ten_localdev01000000000000000 - 2 properties:
ppt_localdevdohaXXXXXXXXXX,ppt_localdevkblXXXXXXXXXXX - 5 departments per property (front_office, housekeeping, maintenance, fnb, security)
- 10 positions (front_desk, housekeeper, maintenance_tech, gm, dept_manager, …)
- 25 staff (mix of email + PIN-only; languages ps/fa/en/ar)
- 1 active
ShiftPatternper position per property - Generated shifts for the next 14 days
The seed is deterministic (uses a fixed PRNG seed for ULIDs). It also writes a JWT + actor card to .secrets/dev-actors/ for use with the API explorer.
7. Running Tests
# unit + application
pnpm -F staff-service test
# unit only (faster)
pnpm -F staff-service test:unit
# integration (Testcontainers — requires Docker)
pnpm -F staff-service test:integration
# sync / offline
pnpm -F staff-service test:sync
# pact contracts (publishes to local broker)
pnpm -F staff-service test:contract
# coverage gate (≥ 85 % overall, ≥ 95 % domain)
pnpm -F staff-service test:coverage
test:integration and test:sync use @testcontainers/postgresql, @testcontainers/redis, and the Pub/Sub emulator container; nothing of the dev compose stack is required.
8. API Exploration
- Swagger UI at
http://localhost:3070/docs. - Postman collection at
services/staff-service/postman/staff-service.postman_collection.json; environment variables included. - A
replis available:pnpm -F staff-service replboots a Node REPL with all use cases wired against the local stack — handy for quickly exercisingPunchClockorAssignStaff.
9. Tracing & Logging Locally
pnpm -F staff-service dev:trace adds an OTLP collector container (otel/opentelemetry-collector:latest) and a Jaeger UI at http://localhost:56686. Set OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:54317 in .env.local to enable.
Logs are JSON in production; locally, a pretty-printer (pino-pretty) is piped automatically.
10. Common Tasks
10.1 Add a new use case
pnpm -F staff-service gen:use-case --name RequestEmergencyOffShift
The generator scaffolds: a domain test, an application use case file, a controller route, an integration test stub, and an OpenAPI fragment. PR-ready in 5 minutes.
10.2 Add a new event subject
pnpm -F staff-service gen:event --topic melmastoon.staff.<aggregate>.<verb_past>.v1
Generates: schema JSON, TypeScript type, outbox helper, and a contract test stub. Updates EVENT_SCHEMAS.md topic table (the doc file has a <!-- generated --> block the generator targets).
10.3 Reset the local DB
pnpm -F staff-service db:reset # drops + recreates + migrates + seeds
10.4 Replay a Pub/Sub event from emulator
pnpm -F staff-service pubsub:replay --subject melmastoon.iam.user.registered.v1 --file fixtures/iam-user-registered.json
11. Hooking Up iam-service and property-service
For full-flow local dev (e.g., termination cascade), bring up iam-service and property-service with their own compose stacks. The monorepo-level pnpm dev:cluster boots all three plus the BFFs. Networking uses host-local ports; cross-service URLs are pre-wired in .env.example.
12. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
dev:deps:up fails on port 55432 | Existing Postgres on host | Edit docker-compose.dev.yml to remap; update .env.local |
| Migrations fail on first run | Postgres not yet ready | Re-run pnpm -F staff-service db:migrate after 5 s |
| Pub/Sub publish hangs | Emulator container not started | pnpm -F staff-service dev:deps:up |
KMS adapter throws ENOENT | Missing .secrets/staff_pin_pepper.local | Re-run setup step 2 (openssl rand) |
Punch returns KMS_UNAVAILABLE | KMS env var pointed to wrong path | Verify KMS_PIN_PEPPER_KEY in .env.local |
| Idempotency conflicts on test reruns | Redis state from previous run | pnpm -F staff-service dev:deps:reset or flush DB 0 |
outbox.spec.ts flaky | Slow Docker on first Testcontainers pull | docker pull postgres:16.4-alpine ahead of time |
If stuck for > 15 min, ping #staff-svc-dev on Slack with the exact command + log tail. Onboarding pair-programming sessions are scheduled weekly (Wednesdays, 14:00 GST).