LOCAL_DEV_SETUP — bff-consumer-service
Sibling: DEPLOYMENT_TOPOLOGY · TESTING_STRATEGY · DATA_MODEL
1. Prerequisites
| Tool | Version | Notes |
|---|---|---|
| Node.js | 20.x LTS | Use nvm / fnm; engines.node = "20.x" enforced in package.json |
| pnpm | 9.x | Workspace package manager |
| Docker Desktop | latest | Required for Postgres + Redis |
| Docker Compose | v2 | Bundled with Docker Desktop |
| Google Cloud SDK | latest | For impersonating bff-consumer-sa against staging when needed |
mkcert | latest | Local TLS for cookie Secure flag testing |
gh CLI | latest | PR helpers |
Repository: this BFF lives in the future application monorepo at services/bff-consumer-service/. The 17 markdown specs live in this documentation repo.
2. One-time setup
# Clone (application monorepo, future state)
gh repo clone ghasitech/ghasi-melmastoon
cd ghasi-melmastoon
# Install workspace
pnpm install --frozen-lockfile
# Set up local TLS for cookie Secure flag testing
mkcert -install
mkcert -cert-file ./certs/local.pem -key-file ./certs/local-key.pem \
bff-consumer.local.melmastoon.test localhost
# Bootstrap env file
cp services/bff-consumer-service/.env.example services/bff-consumer-service/.env.local
Edit .env.local and fill in:
NODE_ENV=development
PORT=8081
LOG_LEVEL=debug
DATABASE_URL=postgres://bff_consumer:bff_consumer@localhost:5440/bff_consumer
REDIS_URL=redis://localhost:6390/0
PUBSUB_PROJECT_ID=local
PUBSUB_EMULATOR_HOST=localhost:8085
BFF_CONSUMER_HANDOFF_HMAC_KEY_CURRENT=dev-rotating-key-do-not-ship-001
BFF_CONSUMER_HANDOFF_HMAC_KEY_PREVIOUS=
BFF_CONSUMER_PEPPER=dev-pepper-do-not-ship
BFF_CONSUMER_RECAPTCHA_SECRET=test-key
UPSTREAM_SEARCH_BASE_URL=http://localhost:8090
UPSTREAM_PRICING_BASE_URL=http://localhost:8091
UPSTREAM_PROPERTY_BASE_URL=http://localhost:8092
UPSTREAM_THEME_BASE_URL=http://localhost:8093
UPSTREAM_TENANT_BASE_URL=http://localhost:8094
UPSTREAM_BFF_TENANT_BOOKING_BASE_URL=http://localhost:8082
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
3. Stand up dependencies
docker compose -f services/bff-consumer-service/docker-compose.dev.yml up -d
# Brings up:
# postgres (5440 → 5432) seeded with tenants, properties, theme peek
# redis (6390 → 6379)
# pubsub-emulator (8085)
# wiremock-search-aggregation (8090) — pre-recorded fixtures
# wiremock-pricing (8091)
# wiremock-property (8092)
# wiremock-theme (8093)
# wiremock-tenant (8094)
# wiremock-bff-tenant (8082) — minimal stub for handoff handshake
# otel-collector + jaeger-ui (16686)
4. Run the service
pnpm --filter @ghasi/service-bff-consumer dev
# starts in watch mode on http://bff-consumer.local.melmastoon.test:8081
Smoke check:
curl -k https://bff-consumer.local.melmastoon.test:8081/bff/consumer/v1/health/ready
curl -k -c /tmp/c.jar -b /tmp/c.jar -H 'Accept-Language: ps-AF' \
-H 'Content-Type: application/json' \
-X POST https://bff-consumer.local.melmastoon.test:8081/bff/consumer/v1/search \
-d '{"geo":{"mode":"city","cityId":"city_kabul"},"checkIn":"2026-05-20","checkOut":"2026-05-22","occupancy":{"adults":2,"children":0,"rooms":1}}'
Trace appears in Jaeger UI at http://localhost:16686.
5. Common commands
| Command | Purpose |
|---|---|
pnpm --filter @ghasi/service-bff-consumer dev | Run with hot reload |
pnpm --filter @ghasi/service-bff-consumer test:unit | Vitest unit tests |
pnpm --filter @ghasi/service-bff-consumer test:integration | Vitest + Testcontainers |
pnpm --filter @ghasi/service-bff-consumer test:contract | Pact consumer + provider |
pnpm --filter @ghasi/service-bff-consumer lint | ESLint + import-boundary rules |
pnpm --filter @ghasi/service-bff-consumer typecheck | tsc --noEmit |
pnpm --filter @ghasi/service-bff-consumer migrate | Drizzle migrations against local Postgres |
pnpm --filter @ghasi/service-bff-consumer migrate:make <name> | Generate new migration |
pnpm --filter @ghasi/service-bff-consumer seed | Seed dev data (tenants, properties, fixture pages) |
pnpm --filter @ghasi/service-bff-consumer openapi | Regenerate openapi.json from controllers |
pnpm --filter @ghasi/service-bff-consumer build | Production build |
docker compose -f services/bff-consumer-service/docker-compose.dev.yml logs -f | Tail dependencies |
6. Working against real upstreams (staging)
For high-fidelity composition tests, point the BFF at staging upstreams while still running locally:
gcloud auth application-default login
gcloud auth print-identity-token --impersonate-service-account=bff-consumer-sa@melmastoon-stage.iam.gserviceaccount.com
# In .env.local override:
UPSTREAM_SEARCH_BASE_URL=https://search-agg.staging.melmastoon.internal
UPSTREAM_PRICING_BASE_URL=https://pricing.staging.melmastoon.internal
# ...
USE_GOOGLE_ID_TOKEN=true
Cloud Armor allow-lists the developer IP for the staging-internal ingress so this works from a corp network.
7. Seed data
pnpm seed populates:
- 5 tenants (
tnt_kabul_grand,tnt_herat_bazaar,tnt_mazar_pearl,tnt_jalalabad_orchard,tnt_bamiyan_cliff). - 60 properties across the 5 tenants.
- 12 theme peeks (one per random property + 5 tenants).
- 200 wishlist rows distributed across 30 anonymous sessions.
- 1 active marketing campaign attribution (
spring-2026). - 50 telemetry rows in the outbox awaiting drain.
The Pub/Sub emulator topics are auto-created on first publish.
8. Useful local URLs
| URL | Purpose |
|---|---|
https://bff-consumer.local.melmastoon.test:8081/bff/consumer/v1/health/ready | Readiness |
http://localhost:16686 | Jaeger trace explorer |
http://localhost:8025 | (optional) Mailhog if SMTP testing wired |
http://localhost:8090/__admin | WireMock admin for search-aggregation-service stubs |
9. Resetting state
docker compose -f services/bff-consumer-service/docker-compose.dev.yml down -v
docker compose -f services/bff-consumer-service/docker-compose.dev.yml up -d
pnpm --filter @ghasi/service-bff-consumer migrate
pnpm --filter @ghasi/service-bff-consumer seed
10. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
ECONNREFUSED on Postgres | Port collision (another Postgres on 5440) | Change services/bff-consumer-service/docker-compose.dev.yml port mapping |
Cookie not set on curl | Secure flag rejected on plain HTTP | Use https URL with mkcert cert or curl -k |
| WireMock fixture missing for upstream call | New endpoint not stubbed | Add fixture to services/bff-consumer-service/test/fixtures/wiremock/<service>/; restart compose |
JWT key not configured startup fail | .env.local missing BFF_CONSUMER_HANDOFF_HMAC_KEY_CURRENT | Re-copy from .env.example |
| Pact verification fails locally | Stale pact in broker; pull latest | pnpm test:contract -- --pull-pacts |