Skip to main content

LOCAL_DEV_SETUP — billing-service

1. Prerequisites

  • Node.js 22 LTS
  • pnpm 9+
  • Docker Desktop (or colima)
  • Google Cloud SDK (gcloud) for Pub/Sub emulator and gcs fake
  • A Postgres 16 image is pulled by Testcontainers; no host install needed

2. Bootstrap

git clone git@github.com:ghasi/melmastoon.git
cd melmastoon/services/billing-service
pnpm install
cp .env.example .env.local
pnpm db:up # docker compose up postgres + pubsub-emulator + gcs-fake
pnpm db:migrate:dev # central schema + 3 demo tenants
pnpm dev # billing-api on :8080

Two-tab dev (recommended):

  • tab 1: pnpm dev
  • tab 2: pnpm dev:drainer (the outbox drainer)

3. .env.local (defaults)

NODE_ENV=development
PORT=8080
LOG_LEVEL=debug

DB_HOST=localhost
DB_PORT=5432
DB_USER=billing
DB_PASSWORD=billing
DB_DATABASE=melmastoon
DB_DEFAULT_SCHEMA=billing_central

PUBSUB_EMULATOR_HOST=localhost:8085
PUBSUB_PROJECT=local-melmastoon

IAM_JWKS_URL=http://localhost:8081/.well-known/jwks.json
IAM_STEPUP_AUDIENCE=billing-service
IAM_DEV_BYPASS=true # accepts a dev token; disabled in CI/staging/prod

AI_ORCHESTRATOR_URL=http://localhost:8090
AI_DEV_FAKE=true # returns deterministic fake responses

FILE_STORAGE_ENDPOINT=http://localhost:4443 # fake-gcs-server
FILE_STORAGE_BUCKET_PATTERN=billing-invoices-{tenantId}

PAYMENT_GATEWAY_URL=http://localhost:8082
PAYMENT_GATEWAY_DEV_FAKE=true # in-memory fake; emits processor-ish events
NOTIFICATION_SERVICE_URL=http://localhost:8083
NOTIFICATION_DEV_FAKE=true

4. Demo tenants

pnpm db:migrate:dev provisions:

Tenant IDCurrencyLocaleCash drawerSharia
t_dev_kabulAFNps-AFyesno
t_dev_jeddahSARar-SAyesyes
t_dev_dushanbeTJStg-TJnono

Each tenant has 2 properties, 5 rooms each, and one in-house reservation already checked-in to exercise the folio path immediately.

5. Make targets

pnpm dev # API
pnpm dev:drainer # outbox drainer
pnpm test # unit + application
pnpm test:integration # spins Testcontainers, full stack
pnpm test:contract # OpenAPI + event schema validation
pnpm test:e2e # Playwright (against local stack)
pnpm lint # eslint + prettier check
pnpm typecheck # tsc --noEmit
pnpm check # lint + typecheck + unit + application
pnpm db:migrate:dev # apply central + tenant migrations
pnpm db:migrate:tenant <tid> # apply per-tenant migrations for one tenant
pnpm seed:fixtures # load fixtures (rate plans, tax rules)
pnpm openapi:emit # regenerate openapi.yaml from controllers
pnpm fixtures:tax-matrix # regenerate tax fixture from tenant overrides

6. Running against the desktop locally

The Electron app (apps/backoffice-desktop) points to http://localhost:8080 when BACKOFFICE_API_BASE is unset. Bring it up with:

cd ../../apps/backoffice-desktop
pnpm dev

A dev token button on the login screen issues a tenant-scoped token signed by the local iam-fake.

7. Working with Pub/Sub locally

Topics are auto-created on first publish. To inspect:

gcloud pubsub topics list --project local-melmastoon
gcloud pubsub subscriptions pull billing.folio.local-debug --auto-ack --limit 10

A pre-wired local-debug subscription on every topic logs to stdout for easy event watching.

8. Hot debugging

  • VS Code launch config Attach to billing-api attaches at port 9229.
  • Domain breakpoints are stable across runs because the domain layer has no dynamic DI.
  • pnpm test:domain --watch is the tightest loop; runs in ~80 ms.

9. Resetting state

pnpm db:nuke # drops all schemas
pnpm db:migrate:dev
pnpm seed:fixtures

10. Cross-references