LOCAL_DEV_SETUP — theme-config-service
Sibling: DEPLOYMENT_TOPOLOGY · TESTING_STRATEGY
Platform anchors:
docs/standards/SERVICE_TEMPLATE.md
This guide walks a new engineer from a clean machine to "service running locally with seeded data, hot reload, and tests green" in under 30 minutes.
1. Prerequisites
| Tool | Version | Why |
|---|---|---|
| Node.js | 22.x LTS | runtime |
| pnpm | 9.x | monorepo package manager |
| Docker | 25+ | local Postgres/Redis/emulators |
| Docker Compose v2 | 2.27+ | service stack |
GCP CLI (gcloud) | latest | optional — only for live integration |
direnv | 2.34+ | env-var loading |
psql | 16.x | DB inspection |
redis-cli | 7.x | cache inspection |
mkcert | latest | local TLS for mTLS endpoints |
Repository root scripts assume bash/zsh; Windows users should use WSL2.
2. Repository layout
ghasi-melmastoon/
├── services/
│ └── theme-config-service/
│ ├── src/
│ │ ├── domain/
│ │ ├── application/
│ │ │ ├── ports/
│ │ │ ├── use-cases/
│ │ │ └── queries/
│ │ ├── infrastructure/
│ │ │ ├── adapters/
│ │ │ ├── persistence/
│ │ │ └── messaging/
│ │ ├── interface/
│ │ │ ├── http/
│ │ │ └── workers/
│ │ └── main.ts
│ ├── test/
│ ├── prompts/
│ ├── evals/
│ ├── contracts/
│ ├── openapi/
│ ├── runbooks/
│ ├── observability/
│ ├── tools/
│ ├── docker-compose.local.yml
│ ├── package.json
│ ├── tsconfig.json
│ └── README.md
└── packages/
├── domain-primitives/
├── eventing/
├── observability/
└── testing-helpers/
3. Bootstrap
git clone https://github.com/ghasi/melmastoon ghasi-melmastoon
cd ghasi-melmastoon
pnpm install
cp services/theme-config-service/.env.example services/theme-config-service/.env.local
direnv allow
.env.local defaults are safe for the local Docker stack (no real GCP credentials required).
4. Local Docker stack
cd services/theme-config-service
docker compose -f docker-compose.local.yml up -d
Started containers:
| Container | Port | Purpose |
|---|---|---|
theme-pg | 5432 | Postgres 16 |
theme-pgbouncer | 6432 | pooler (optional; service can use 5432 directly) |
theme-redis | 6379 | Memorystore stand-in |
theme-pubsub | 8085 | Pub/Sub emulator |
theme-gcs | 4443 | fake-gcs-server |
theme-cdn-mock | 8089 | mock CDN invalidation API |
theme-ai-mock | 8090 | AI orchestrator stub (cassettes) |
theme-otel | 4318 | OTel collector to local Jaeger + Prom |
Healthcheck: pnpm run dev:health waits until all containers report ready.
5. Database
pnpm run db:create # creates 'theme_config' database + roles
pnpm run db:migrate # applies Drizzle migrations
pnpm run db:seed # platform default scaffold + 3 demo tenants
pnpm run db:rls:verify # asserts RLS policies present on every tenant-scoped table
Demo tenants:
| Tenant | Default locale | Theme aesthetic | Notes |
|---|---|---|---|
tnt_dev_kabul_continental | ps-AF | warm earth, primary #0F4C81 | RTL+LTR mix, 3 content blocks |
tnt_dev_panjsher_lodge | fa-AF | mountain palette, primary #13A3A1 | RTL, video-hero variant |
tnt_dev_test_chain_phase2 | en-US | minimal | LTR, used to exercise property-scoped Phase 2 |
Each tenant has one published version + one draft so the editor surface has something to render.
6. Run the service
pnpm run dev # NestJS hot reload on http://localhost:7041
pnpm run dev:workers # spawns outbox-publisher + inbox-consumers + sweeper jobs in one process
OpenAPI: http://localhost:7041/docs. Health: http://localhost:7041/healthz. Metrics: http://localhost:7041/metrics.
7. Useful scripts
| Command | What it does |
|---|---|
pnpm run lint | ESLint + Prettier check |
pnpm run typecheck | tsc --noEmit |
pnpm run test | Vitest unit + application tests |
pnpm run test:integration | Vitest integration with Testcontainers |
pnpm run test:http | supertest HTTP suite |
pnpm run test:contract:openapi | spectral against the platform ruleset |
pnpm run test:contract:events | ajv against event fixtures |
pnpm run test:evals -- palette.contrast | run a specific AI eval suite |
pnpm run db:reset | drop + recreate + migrate + seed |
pnpm run pubsub:tail melmastoon.theme.events | tail the local emulator |
pnpm run gcs:ls | list local fake-gcs bundles |
pnpm run cli:publish-demo | publishes a sample new draft to exercise the full flow |
pnpm run cli:build-bundle <versionId> | builds & prints the bundle JSON without persisting |
pnpm run openapi:generate | regenerates openapi/theme-config-service.openapi.yaml |
pnpm run openapi:diff | diffs against main's spec; fails on breaking change |
8. Frontend integration (optional)
To exercise authoring against the local backoffice:
cd ../bff-backoffice-service && pnpm run dev
cd ../../apps/backoffice-console && pnpm run dev
The console points at http://localhost:7041 by default in apps/backoffice-console/.env.local.
To see the public booking flow render the published bundle:
cd apps/booking-flow && pnpm run dev # opens http://localhost:5173
The booking flow reads http://localhost:7041/public/themes/<themeId>/published.json.
9. Working with AI surfaces
The theme-ai-mock container replays cassettes from services/theme-config-service/evals/cassettes/. To record new cassettes against the real orchestrator:
export AI_MODE=record
export AI_ORCHESTRATOR_URL=https://ai-orchestrator.dev.melmastoon.app
gcloud auth print-identity-token # for orchestrator mTLS
pnpm run dev
Generated cassettes go into evals/cassettes/<surface>/<test-name>.json and should be committed.
10. Common gotchas
app.tenant_idnot set: queries return zero rows due to RLS. UsewithTenantCtx(...)in tests; in REPL setSET app.tenant_id = 'tnt_dev_kabul_continental';first.- OCC mismatch in tests: the test builder
aTheme().buildPersisted()returns the saved aggregate; use that, not the input. - Bundle SHA differs across runs: check Node version; the canonicaliser depends on stable JSON ordering. Re-run after
nvm use. - Pub/Sub emulator port collision: ensure no other Pub/Sub emulator is running (
lsof -i :8085). - Memorystore connection refused: the local Redis sometimes takes ~ 3 s to listen; the dev script retries.
- Time-zone surprises: all timestamps are UTC; if the editor shows a wrong "published at", the issue is likely the BFF, not this service.
11. Editor setup
- VS Code extensions:
ms-azuretools.vscode-docker,bradlc.vscode-tailwindcss(for the editor app, not this service),Vitest.explorer,Prisma.prisma(for DB schema viewing only). - Recommended
.vscode/settings.json:{"editor.formatOnSave": true,"eslint.workingDirectories": [{ "pattern": "services/theme-config-service" }],"vitest.commandLine": "pnpm run test --"}
12. Where to go next
- Read
SERVICE_OVERVIEWandDOMAIN_MODEL. - Skim
APPLICATION_LOGIC §2for the use-case map. - Try
pnpm run cli:publish-demoand watch logs in the dev terminal — that one command exercises the bulk of the service.
13. References
- Repo layout:
docs/standards/SERVICE_TEMPLATE.md - Platform local dev:
ghasi-melmastoon/README.md