Skip to content

feat: add local docker compose development stack - #144

Merged
3m1n3nc3 merged 2 commits into
learnault:mainfrom
Fabluchy:feat/docker-compose-dev-stack
Aug 21, 2026
Merged

feat: add local docker compose development stack#144
3m1n3nc3 merged 2 commits into
learnault:mainfrom
Fabluchy:feat/docker-compose-dev-stack

Conversation

@Fabluchy

Copy link
Copy Markdown
Contributor

Closes #125

Summary

Adds a reproducible local Docker Compose development stack — API, wallet-provisioning worker, PostgreSQL, and Redis — started with one command. While building it, I found and fixed several things that blocked local development entirely (fresh-install Prisma failures, a crash on boot under real Node ESM, and a migration that always fails on a fresh database).

Changes

Docker Compose stack (#125)

  • docker-compose.ymlapi, worker, db (Postgres 16), redis services
    • Health checks on every service (pg_isready, redis-cli ping, /health/live) with depends_on: service_healthy so readiness waits for dependencies
    • API entrypoint applies migrations and seeds deterministic fixtures on boot; worker entrypoint runs prisma migrate deploy before starting
    • Named volumes (pgdata, redisdata) — reset is project-scoped via docker compose down -v
    • stop_grace_period: 30s matching the app's graceful-shutdown handler
  • docker/Dockerfile.dev — dev image: all deps, OpenSSL (for Prisma), generated client; source bind-mounted for nodemon hot reload
  • docker/entrypoint-dev-api.sh / docker/entrypoint-dev-worker.sh — explicit migrations + seed, then dev server / worker
  • src/workers/wallet-provisioning.worker.ts — polls the idempotent wallet-provisioning outbox (Prisma repo + dev in-memory KMS + SDK keypair generator), with SIGTERM/SIGINT graceful shutdown
  • scripts/stack-smoke-test.sh — validates compose config, starts the stack, waits for /health/ready, probes liveness/readiness, prints service status (pnpm stack:smoke; --validate for config-only)
  • package.jsonstack:up/down/reset/logs/validate/smoke, worker:dev, db:generate, db:deploy
  • docs/DEVELOPMENT_STACK.md — quick start, health checks, reset, logs, smoke test, troubleshooting; linked from README
  • .env.examplePOSTGRES_*, REDIS_PORT, API_PORT, WORKER_POLL_INTERVAL_MS
  • CI: validates docker compose config --quiet on every PR

Fixes found while building the stack

  • prisma.config.tsprisma generate failed without a DATABASE_URL (Prisma's env() helper throws), which also meant the production Dockerfile build stage could never generate the client. Now falls back to a local default; seed command moved into the Prisma 7 config (migrations.seed)
  • src/config/jwt.tsimport { JsonWebTokenError } from 'jsonwebtoken' crashes the server under real Node ESM (The requested module 'jsonwebtoken' does not provide an export named 'JsonWebTokenError'). Uses the default import instead, matching src/utils/jwt.ts
  • Migration ordering20260718000000_add_learner_preferences referenced users before 20260719100042_first_migration created it, so prisma migrate deploy always failed on a fresh database (P3009). Renamed to 20260719100043_add_learner_preferences (the old one never applied anywhere — it always failed). 100% rename, no SQL change
  • Dockerfile — healthcheck probed /health, which 404s (routes are /health/live + /health/ready); now probes /health/live
  • Removed accidentally committed UTF-16 error-log scratch files (server_error*.txt, lint_results*.txt, lint_output.json) — already excluded by .dockerignore

Verification

  • pnpm lint — clean
  • tsc --noEmit — clean
  • pnpm test:ci — 641 passed / 3 skipped (pre-existing DB-requiring skips; no Postgres in unit-test env)
  • docker compose config --quiet — valid
  • Full smoke test from a clean state (docker compose down -v && scripts/stack-smoke-test.sh):
    • db/redis healthy, api healthy, worker running
    • GET /health/live{"status":"ok",...}
    • GET /health/ready{"status":"ready","checks":{"database":"ok"}}
    • Seed deterministic fixtures verified in Postgres (6 users, 8 modules, 16 completions, 14 credentials); re-running is idempotent
    • docker compose stop api → graceful shutdown observed; down -v removes only project volumes

Fabluchy and others added 2 commits August 21, 2026 00:02
Provide a one-command local stack (API, wallet-provisioning worker,
PostgreSQL, Redis) with health checks, dependency startup, explicit
migrations + deterministic seed, project-scoped volumes, and a smoke
test. Fixes blocked local development:

- prisma.config.ts: fall back to a local DATABASE_URL so `prisma
  generate` succeeds without a live database (also unblocks the Docker
  build), and move the seed command into the Prisma 7 config
- package.json: add predev/prebuild prisma generate hooks, db:generate,
  db:deploy, worker:dev, and stack:* scripts
- src/config/jwt.ts: import JsonWebTokenError via the jsonwebtoken
  default export so the server boots under real Node ESM
- reorder the learner-preferences migration after first_migration
  (it referenced `users` before that table existed, failing fresh DBs)
- Dockerfile: point the healthcheck at /health/live instead of the
  404ing /health path; drop accidentally committed UTF-16 error logs
- CI: validate `docker compose config` on every PR

Closes learnault#125
@3m1n3nc3
3m1n3nc3 merged commit 587f7c6 into learnault:main Aug 21, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: Add Local Docker Compose Development Stack

2 participants