Skip to content

Development

kneshi edited this page May 8, 2026 · 3 revisions

Local Development

For production deployment (GHCR pull or build-from-source), see Production.

The 6 commands in the README are the canonical setup. Below is the same flow, with each step annotated.

Step-by-step

pnpm install

Installs all workspace deps (backend, frontend, shared) at once via pnpm workspaces. Run from repo root.

cp .env.dev.example .env.dev

.env.dev is gitignored. The example ships with working dev defaults - every secret literal contains dev-only-not-for-production or _dev so misuse in a real environment is greppable. No edits required for local dev.

ln -s ../.env.dev backend/.env

NestJS and Prisma both look for .env in their own working directory. pnpm db:migrate (which delegates to prisma migrate dev inside backend/) runs with cwd = backend/, so it needs backend/.env. The symlink keeps a single source of truth at the repo root.

docker compose -f docker-compose.yml -f build/dev.compose.yml --env-file .env.dev up -d postgres redis rustfs mailpit

Brings up the four datastore/dev-tool containers in the background:

  • postgres - Postgres 18 on 127.0.0.1:5432
  • redis - Redis 8 on 127.0.0.1:6379 (started with requirepass)
  • rustfs - S3-compatible object store on 127.0.0.1:9000 (console at 9001)
  • mailpit - SMTP capture on 127.0.0.1:1025 (web UI at 8025)

The two -f flags overlay the dev-specific bits (mailpit, dev Dockerfile target, NODE_ENV=development, etc.) onto the prod-correct base. --env-file .env.dev provides the secrets the compose files reference. The backend and frontend containers from the compose files are deliberately not started here - pnpm dev (next step) runs them on the host with hot reload.

pnpm db:migrate && pnpm seed

Applies all Prisma migrations to the freshly-initialised Postgres database, then seeds GDPR recitals (173) + articles (99) in 5 languages, default RSS feeds, and the default Organization row. Re-runnable: migrations are idempotent, the seed upserts.

pnpm dev

Starts the backend (NestJS, port 3001) and frontend (Next.js, port 3000) in parallel, both with watch mode.

Service URL
Frontend http://localhost:3000
Backend API http://localhost:3001
Swagger docs http://localhost:3001/api/docs
RustFS console http://localhost:9001
MailPit inbox http://localhost:8025

Stopping the stack

Ctrl+C on pnpm dev stops the host-side dev servers but leaves the docker datastores running. To stop everything:

docker compose -f docker-compose.yml -f build/dev.compose.yml --env-file .env.dev stop

Or to stop AND remove containers (volumes preserved - DB data and uploaded objects survive):

docker compose -f docker-compose.yml -f build/dev.compose.yml --env-file .env.dev down

To wipe volumes too (full reset; you'll need to re-run migrations + seed afterwards):

docker compose -f docker-compose.yml -f build/dev.compose.yml --env-file .env.dev down -v

Common issues

EADDRINUSE on port 3000 or 3001 after a crashed run:

lsof -ti tcp:3000 | xargs -r kill -9
lsof -ti tcp:3001 | xargs -r kill -9

Production build instead of watch mode: swap pnpm dev for pnpm dev:prod.

Skip seed (DB already populated): drop the && pnpm seed from the migrate step.

Dockerized dev (alternative)

If you prefer to run everything in containers - for example, you don't have Node/pnpm installed locally, or you want to reproduce a container-only issue with hot reload - overlay the dev compose file. Export the compose-file pair once so you don't have to repeat the -f / --env-file flags on every command:

export COMPOSE_FILE='docker-compose.yml:build/dev.compose.yml'
export COMPOSE_ENV_FILES='.env.dev'

docker compose up --build

build/dev.compose.yml adds MailPit, switches the backend and frontend Dockerfiles to their dev target, mounts source dirs for hot reload, and clobbers the env values that must differ from prod (NODE_ENV=development, COOKIE_SECURE=false, SMTP_HOST=mailpit, etc.).

First-time setup (the dev container runs pnpm dev, not the prod CMD, so you apply migrations and seed yourself):

docker compose exec backend npx prisma migrate deploy
docker compose exec backend pnpm seed

Trade-offs: bind-mount filesystem performance on macOS and WSL is slower than native, and only src/ and prisma/ are mounted - edits to root-level config (next.config.ts, tsconfig.json, package.json, etc.) need a container rebuild. The host-side flow above remains the recommended default.

Seed data

pnpm seed upserts GDPR recitals and articles into PostgreSQL, ensures a default Organization row exists, and inserts default RSS feeds for the regulatory watch module (CNIL and EDPB).

GDPR Regulation

Sources live under backend/prisma/seed/:

  • rgpd.json - metadata and 173 recitals in 5 languages (FR, EN, ES, DE, IT)
  • gdpr-articles.json - 99 GDPR articles in 5 languages with chapter grouping

Reference data (dropdowns, all bilingual FR/EN)

  • Guarantee types: SCC, BCR, Adequacy decision, Code of conduct, Certification, Derogations (Art. 49)
  • Person categories: Employees, Internal services, Clients, Suppliers, Service providers, Prospects, Candidates
  • Recipient types: Internal service, Subcontractors, Third country recipients, Partners
  • Security measures: Traceability, Software protection, Backup, Encryption, Access control, Subcontractor control
  • Adequate countries: Andorra, Argentina, Canada, Guernsey, Isle of Man, Faroe Islands, Israel, Jersey, New Zealand, Switzerland, Uruguay

Project Structure

article30/
├── shared/                      # shared TypeScript types & constants (workspace)
├── backend/                     # NestJS API, port 3001 (workspace)
│   ├── src/modules/             # feature modules (treatments, dsr, vendors, ...)
│   ├── prisma/
│   │   ├── schema.prisma        # source of truth for the data model
│   │   ├── migrations/          # generated SQL migrations
│   │   ├── seed.ts              # `pnpm seed` entrypoint
│   │   ├── seed/                # rgpd.json, gdpr-articles.json
│   │   └── pdfs/                # generated PDF assets
│   └── Dockerfile               # multi-stage (dev + prod targets)
├── frontend/                    # Next.js app, port 3000, App Router (workspace)
│   └── Dockerfile               # multi-stage (dev + prod targets)
├── build/                       # compose overlays for local builds
│   ├── dev.compose.yml          # dev overlay (mailpit, dev Dockerfile target, hot reload)
│   └── prod.compose.yml         # build-from-source prod overlay
├── .github/                     # workflows, dependabot.yml, issue templates
├── .husky/                      # commit-msg (commitlint) + pre-commit hooks
├── docker-compose.yml           # prod stack (pulls images from GHCR)
├── .env.dev.example             # dev env template (cp to .env.dev, works as-is)
├── .env.prod.example            # prod env template (fill secrets manually)
├── pnpm-workspace.yaml          # pnpm workspaces config
├── package.json                 # root scripts; per-workspace package.json under each subdir
├── release-please-config.json   # SemVer + CHANGELOG generation
├── commitlint.config.cjs        # Conventional Commits enforcement
├── CHANGELOG.md                 # release-please managed; do not hand-edit
├── CONTRIBUTING.md              # contributor entry point
├── SECURITY.md                  # vulnerability reporting channel
├── README.md
└── LICENSE

docs/, samples/, AGENTS.md, CLAUDE.md, TODO.md, and tooling dirs (.claude/, .gitnexus/, .serena/) are gitignored by design (per-developer notes, auto-generated PDF samples, agent configs); they don't appear in a fresh clone.

Clone this wiki locally