property-service — LOCAL_DEV_SETUP
Companion: DEPLOYMENT_TOPOLOGY · DATA_MODEL · API_CONTRACTS · TESTING_STRATEGY
This document is the binding "first 10 minutes" recipe for running property-service locally. The goal is: clone → pnpm i → docker compose up → service responding on :3010 with seeded data, fully independent of cloud credentials.
Prerequisites: Node.js 20.x, pnpm 9.x, Docker 24+, GNU make. Windows users: use WSL2 for the docker compose stack.
1. Stack
docker-compose.dev.yml (lives in services/property-service/):
| Container | Image | Port | Purpose |
|---|---|---|---|
pg | postgis/postgis:15-3.4 | 5432 | Cloud SQL surrogate; PostGIS preinstalled |
redis | redis:7-alpine | 6379 | Memorystore surrogate |
pubsub | gcr.io/google.com/cloudsdktool/cloud-sdk:emulators | 8085 | Pub/Sub emulator |
wiremock-iam | wiremock/wiremock | 8081 | iam-service JWKS + OPA bundle stubs |
wiremock-files | wiremock/wiremock | 8082 | file-storage-service signed-URL + scan-event stubs |
wiremock-ai | wiremock/wiremock | 8083 | ai-orchestrator-service capability stubs |
wiremock-geo | wiremock/wiremock | 8084 | geo-service geocoding stubs |
mailpit | axllent/mailpit | 8025 | Captures any system notifications (audit emails) |
The service itself runs on the host (pnpm dev) so file-watch is fast; the docker stack is only the dependencies.
2. Bootstrap
cd services/property-service
cp .env.example .env.development # already populated with localhost values
pnpm install
docker compose -f docker-compose.dev.yml up -d
pnpm db:migrate # runs all migrations against local pg
pnpm db:seed # loads the demo seed (next section)
pnpm dev # NestJS watch mode on :3010
Healthcheck:
curl -s http://localhost:3010/healthz # { "status": "ok" }
curl -s http://localhost:3010/readyz # { "status": "ready", "checks": {...} }
The dev JWT issuer (wiremock) signs tokens with a dev JWK; mint a dev token via:
pnpm run dev:jwt -- --tenant tnt_dev_kabul --user usr_dev_admin --roles tenant.admin
# → prints a token; export AUTH_TOKEN=<token>
3. Seed Data
pnpm db:seed produces a deterministic, ULID-stable dataset:
3.1 Tenants (provisioned in the dev tenant-service mock)
tnt_dev_kabul— Kabul, AF, localeps-AFdefault +en,fa, currencyAFNtnt_dev_dushanbe— Dushanbe, TJ, localetg-TJdefault +ru,en, currencyTJStnt_dev_tehran— Tehran, IR, localefa-IRdefault +en, currencyIRR
3.2 Properties (3, one per tenant)
| Property ID | Tenant | Display name (default locale) | Geo | Status |
|---|---|---|---|---|
ppt_dev_kabul_01 | tnt_dev_kabul | "د کابل میلمستون" / "Kabul Melmastoon" | (34.5553, 69.2075) | published |
ppt_dev_dushanbe_01 | tnt_dev_dushanbe | "Меҳмонхонаи Душанбе" | (38.5598, 68.7870) | published |
ppt_dev_tehran_01 | tnt_dev_tehran | "مهمانخانه تهران" | (35.6892, 51.3890) | draft (intentionally — to exercise the publish flow) |
Each published property has:
- 5 room types (
KING,TWIN,SUITE,FAMILY,DORM— Tehran hasAPTinstead ofDORM) - 10 rooms per property (30 total)
- ≥ 1 hero photo + 4 supporting photos (synthetic small JPEGs in
seeds/photos/) - Regional amenities seeded:
wifi,breakfast,halal_kitchen,prayer_room,generator_backup,hot_water_24h(hot_water_scheduledfor Dushanbe),women_only_floor(Kabul),airport_shuttle - Resolved policy: check-in 14:00, check-out 12:00, cancellation
24h_full_refund, childwelcome, smokingno, petsno(dogscase_by_casefor Tehran) - 1 OOO room per property to demonstrate the OOO/RTS flow
- Tehran property is in
draftto demonstrate publish invariants
3.3 Synthetic photos
Pre-built tiny JPEGs (1024×768, ~50 KB each) live in seeds/photos/ and are uploaded to the dev file-storage-service mock during seed; the mock returns verdict=clean after a 2 s delay.
3.4 Synthetic AI runs
pnpm db:seed:ai (optional) stages one accepted and one pending AI suggestion per property to demonstrate the HITL UI.
4. Common Commands
| Command | Purpose |
|---|---|
pnpm dev | NestJS watch mode |
pnpm test | Unit tests |
pnpm test:int | Integration tests (Testcontainers spins its own pg/redis/pubsub) |
pnpm test:e2e | Service-local E2E (uses the running docker compose) |
pnpm lint | eslint |
pnpm typecheck | tsc --noEmit |
pnpm db:migrate | Apply migrations |
pnpm db:migrate:down | Apply paired down.sql for the latest migration |
pnpm db:seed | Load demo data |
pnpm db:reset | Drop schema + reseed |
pnpm openapi:check | Lint + breaking-change diff vs published spec |
pnpm asyncapi:check | Lint + schema-registry diff for events |
pnpm contracts:verify | Run Pact provider verification against the local registry |
Make targets mirror the most common ones (make dev, make test, make seed).
5. Mocked External Calls
- JWT issuer / JWKS / OPA bundle →
wiremock-iam. Stubs inmocks/iam/. Includes ajwks.json, a sample OPA bundle, and a/v1/data/property/decisionsendpoint. - File storage signed URLs →
wiremock-files. The signed URL points back atwiremock-filesitself, which "accepts" the upload and immediately publishes amedia.asset.scanned.v1to the Pub/Sub emulator withverdict=clean. To exercise the infected path, usepnpm dev:scan-infected <photoId>. - AI orchestrator →
wiremock-ai. Stubs for each capability return canned outputs derived from the input property to keep tests deterministic. Override per scenario inmocks/ai/scenarios/. - Geo service →
wiremock-geo. High-confidence geocodes for the three seeded cities; low-confidence stub at addressunknown street, nowhereto exercise AI fallback.
6. Useful URLs (running stack)
- Service: http://localhost:3010
- OpenAPI UI: http://localhost:3010/docs
- Healthz / readyz: http://localhost:3010/healthz · /readyz
- Mailpit UI: http://localhost:8025
- Postgres (psql):
postgres://property:property@localhost:5432/property_dev - Redis:
redis://localhost:6379 - Pub/Sub emulator:
localhost:8085(PUBSUB_EMULATOR_HOST=localhost:8085)
7. Tearing Down
docker compose -f docker-compose.dev.yml down -v # stops + removes volumes
pnpm db:reset # only needed if you want to keep the stack but reset data
8. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
pnpm dev exits with JWKS_FETCH_FAILED | wiremock-iam not up | docker compose up -d wiremock-iam |
Migrations stall on CREATE EXTENSION postgis | wrong image | confirm postgis/postgis:15-3.4 |
404 PROPERTY_NOT_FOUND after seed | wrong tenant context | mint a fresh dev JWT with the matching tenant |
Photos stuck uploaded | wiremock-files scan stub off | docker compose restart wiremock-files |
| Pub/Sub emulator host not set | env not exported in shell | export PUBSUB_EMULATOR_HOST=localhost:8085 |
| RLS surprise: empty list | app.tenant_id not set | service reset; ensure dev token includes tenant_id |
When the dev environment differs from production (e.g., running without VPC connectivity), the service auto-detects via
NODE_ENV !== 'production'and relaxes only the controls explicitly enumerated above.