Files
apf_portal/docs/development.md
T
Julien Gautier b69d3a2206
CI / check (push) Failing after 2s
CI / commits (push) Has been skipped
CI / perf (push) Failing after 12s
CI / a11y (push) Failing after 2s
CI / scan (push) Failing after 1m44s
docs: add development.md as the day-to-day reference for working on apf_portal
A new contributor (or returning lead) opening the repo gets:
- the final repo layout, with one-line annotations per top-level dir
- the prerequisite tooling list (Node 24 LTS, pnpm 10, mkcert,
  optional local Trivy/gitleaks, Docker for Postgres)
- the fresh-clone setup steps (clone, pnpm install, prisma generate,
  sanity check)
- the daily commands organised by intent: serve, test (incl. single
  file), lint, build, generate (apps / libs / components), Prisma,
  the four ci:* scripts that mirror the CI gates
- the conventional commit cycle end-to-end (branch naming, hook
  enforcement, PR gates, squash-merge, release tagging)
- a 'where to look' table cross-linking the project rules
  (CLAUDE.md), the ADRs, the setup guides, and the personal notes
- an explicit 'to be added' section listing what the doc will grow
  into (local infra Docker Compose, auth dev-loop, component
  patterns, debugging tips, release workflow, Renovate policy)

The doc is intentionally non-exhaustive at v1 - it captures what a
contributor needs today and is structured to grow as the workflow
sharpens. Indexed in docs/README.md under a new 'Daily development'
section, separate from the one-off onboarding guides under
docs/setup/.
2026-04-30 19:58:37 +02:00

13 KiB

Development guide

This document is the day-to-day reference for working on apf_portal. It covers the repo layout, the prerequisites, the initial setup from a fresh clone, and the commands you'll run during a typical development cycle. It is meant to grow — add sections as the team's workflow does.

For decision rationale, see the ADRs. For onboarding the local environment (terminal, Node, pnpm), see setup/.


1. Repo layout

apf_portal/
├── .gitea/workflows/             # CI pipelines (ADR-0015)
│   ├── ci.yml                    # per-PR + push to main: check / scan / commits / perf / a11y
│   └── security-scheduled.yml    # weekly full-tree scan + prod Lighthouse
├── .github/                      # Nx AI-tooling skills, prompts, agents (Nx-managed)
├── .husky/                       # local git hooks (ADR-0007)
│   ├── pre-commit                # → pnpm exec lint-staged
│   └── commit-msg                # → pnpm exec commitlint
├── apps/
│   ├── portal-shell/             # Angular 21 SPA (zoneless, standalone, Signals, Vitest, SCSS)
│   │   ├── public/               # static assets
│   │   ├── src/                  # entry, app config, routes, styles
│   │   ├── postcss.config.js     # Tailwind PostCSS plugin
│   │   └── project.json          # Nx project config (build, serve, test, lint targets)
│   ├── portal-shell-e2e/         # Playwright e2e for portal-shell
│   ├── portal-bff/               # NestJS 11 BFF (Express adapter, ValidationPipe, Jest)
│   │   ├── src/                  # main, app module, controllers, services
│   │   ├── prisma/schema.prisma  # Prisma 7 schema (postgresql)
│   │   ├── prisma.config.ts      # Prisma 7 TS config (loads DATABASE_URL from .env)
│   │   ├── .env.example          # env-vars catalog (committed); .env stays gitignored
│   │   └── project.json
│   └── portal-bff-e2e/           # Jest e2e for portal-bff
├── libs/
│   ├── feature/<name>/           # vertical feature libs (e.g. feature-auth)
│   └── shared/<scope>/           # cross-cutting libs (tokens, ui, util)
├── docs/
│   ├── README.md                 # doc index
│   ├── decisions/                # ADRs (MADR 4.0.0)
│   ├── setup/                    # local-environment onboarding (Zsh, pnpm, Nx workspace)
│   └── development.md            # this file
├── notes/                        # personal scratchpad (gitignored)
├── CLAUDE.md                     # project rules + architecture summary
├── commitlint.config.cjs         # Conventional Commits config
├── eslint.config.mjs             # workspace ESLint with module boundaries
├── lighthouserc.js               # Lighthouse CI thresholds (ADR-0017)
├── nx.json                       # Nx workspace config
├── package.json                  # workspace deps + scripts
├── pnpm-workspace.yaml           # apps/* + libs/**
├── tsconfig.base.json            # shared TS strict config
└── vitest.workspace.ts           # Vitest workspace projects

The conventions that govern this layout are recorded in:

  • ADR-0002 — Nx workspace shape
  • ADR-0003 — naming convention (portal-shell, portal-bff, feature-<name>, shared-<scope>)
  • ADR-0007 — local hooks
  • ADR-0015 — CI/CD shape

2. Prerequisites

A working dev machine for apf_portal needs:

Tool Why How
WSL 2 + Debian (Windows) or Linux/macOS native All commands assume a POSIX shell see setup/01
Node.js 24 (latest LTS) Runtime, pinned in .nvmrc nvm install 24 && nvm use (see setup/02)
pnpm 10+ Mandatory package manager (no npm/yarn lockfile) corepack enable && corepack prepare pnpm@latest --activate
Git ≥ 2.40 Husky 9 + signed commits eventually usually default
mkcert Local HTTPS for cookie-prefix __Host- (ADR-0009) apt install mkcert then mkcert -install
Trivy (optional, locally) Dep vuln scan when running ci:scan locally; CI uses an action apt install trivy or trivy install docs
gitleaks (optional, locally) Same pattern; CI uses an action apt install gitleaks or gitleaks install docs
Docker For Postgres + Redis containers in dev (until on-prem infra ADR lands) Docker Desktop on Windows, Docker Engine on Linux

Work inside the WSL filesystem (~/Works/...), never /mnt/c/... — the latter has severe I/O penalties that break Nx caching.


3. Initial setup from a fresh clone

git clone gitea@git.unespace.com:julien/apf_portal.git
cd apf_portal

# Install deps (also runs `husky` to wire git hooks)
pnpm install

# Generate the Prisma client (until you set up the DB it errors on
# missing DATABASE_URL — that's expected; the generation only reads
# the schema, not the DB).
cd apps/portal-bff && pnpm exec prisma generate && cd ../..

# Sanity check
pnpm nx run-many -t lint test build

For the BFF to actually run end-to-end, you'll also need:

# .env from .env.example, then fill in DATABASE_URL with your local Postgres
cp apps/portal-bff/.env.example apps/portal-bff/.env

# Local Postgres via Docker (one-liner; will be replaced by a Docker Compose file in a later doc)
docker run -d --name apf-postgres -p 5432:5432 \
  -e POSTGRES_USER=portal -e POSTGRES_PASSWORD=portal -e POSTGRES_DB=portal_dev \
  postgres:17-alpine

A proper local-dev infra spec (Postgres HA-like, Redis, OTel collector) will land with the on-prem infrastructure ADR; in the meantime the one-liner above is sufficient to run the BFF.


4. Daily commands

Run the apps

pnpm nx serve portal-shell        # http://localhost:4200 (Angular dev server)
pnpm nx serve portal-bff          # http://localhost:3000/api (NestJS)

Both can run in parallel in two terminals; the SPA proxies API calls to the BFF in dev.

Test

pnpm nx test portal-shell         # Vitest (single run; --configuration=watch for watch mode)
pnpm nx test portal-bff           # Jest

pnpm nx run-many -t test          # all projects
pnpm nx affected -t test          # only projects affected since main

Run a single Vitest file:

pnpm nx test portal-shell --testFile=src/app/app.spec.ts

Lint, type-check, format

pnpm nx lint portal-shell             # one project
pnpm nx run-many -t lint              # all projects
pnpm nx affected -t lint              # affected only
pnpm nx affected -t lint --fix        # auto-fix where possible

pnpm nx affected -t type-check        # explicit type-check (independent of test/build)

pnpm nx format:write                  # apply Prettier
pnpm nx format:check                  # CI-style verification

Build

pnpm nx build portal-shell                                   # development build
pnpm nx build portal-shell --configuration=production        # production build
pnpm nx run-many -t build
pnpm nx affected -t build

Generate

# Component in portal-shell
pnpm nx g @nx/angular:component <name> --project=portal-shell --standalone

# Service / controller / module in portal-bff
pnpm nx g @nx/nest:service <name> --project=portal-bff
pnpm nx g @nx/nest:controller <name> --project=portal-bff

# New shared lib (TS-only, consumable by both apps)
pnpm nx g @nx/js:library --name=shared-<scope> --directory=libs/shared/<scope> \
  --bundler=tsc --unitTestRunner=vitest \
  --tags="scope:shared,type:shared" --no-interactive

# New Angular feature lib (front-only)
pnpm nx g @nx/angular:library --name=feature-<name> --directory=libs/feature/<name> \
  --standalone=true --unitTestRunner=vitest-analog \
  --tags="scope:portal-shell,type:feature" --no-interactive

Sweep generated files for process.env.X (dot notation) → process.env['X'] (bracket notation), required by the strict-TS option noPropertyAccessFromIndexSignature: true. The Nx generators don't emit bracket form.

Prisma

# Regenerate the typed client after schema changes
cd apps/portal-bff && pnpm exec prisma generate && cd ../..

# Create and apply a migration in dev
cd apps/portal-bff && pnpm exec prisma migrate dev --name <migration-name> && cd ../..

# Deploy migrations in prod (run by deploy pipeline, not locally)
cd apps/portal-bff && pnpm exec prisma migrate deploy && cd ../..

# Inspect the dev DB
cd apps/portal-bff && pnpm exec prisma studio && cd ../..

CI scripts (runnable locally)

Mirror what the CI does on every PR:

pnpm ci:check      # nx affected -t format:check lint test build
pnpm ci:audit      # pnpm audit --audit-level=moderate
pnpm ci:commits    # commitlint on the PR commit range (uses $COMMIT_LINT_FROM, defaults to origin/main)
pnpm ci:perf       # production build + Lighthouse CI against the static-served bundle

ci:scan (Trivy + gitleaks) is currently invoked from CI YAML rather than as a pnpm script — those tools are Go binaries without clean npm wrappers. Run them locally if you've installed the binaries.


5. Conventional commit cycle

  1. Branch from main with a short slug:

    git switch -c feat/portal-shell/auth-login   # or fix/..., chore/..., docs/...
    
  2. Commit using Conventional Commits. The local commit-msg hook (commitlint) rejects anything else.

    feat(portal-shell): add login flow stub
    fix(portal-bff): correct env var bracket access
    chore: bump @nx/* to 22.7.2
    docs(decisions): add ADR-0018 for security baseline
    

    pre-commit runs lint-staged → Prettier on staged files. Lint and tests stay in CI.

  3. Push and open a PR against main. The CI runs:

    • check (lint, type-check, test, build on affected)
    • scan (audit, Trivy, gitleaks)
    • commits (commitlint on the PR commit range)
    • perf (Lighthouse on the production bundle)
    • a11y (axe-core; placeholder until first real screens)

    All five must be green to merge. PR title must itself be a Conventional Commits message — it becomes the squash-merge subject (ADR-0015).

  4. Squash-merge into main. Branch is auto-deleted. Linear history maintained.

  5. To cut a release: tag vX.Y.Z on main. The release.yml workflow will pick it up (currently a stub; populated alongside the on-prem deploy ADR).


6. Where to look

Question Doc
Project rules and the why behind them CLAUDE.md
All ADRs (decisions index) docs/decisions/README.md
Initial environment setup (Zsh, Node, pnpm) docs/setup/
RSSI briefing for ASVS / HDS / etc. notes/asvs-level-decision-briefing-rssi.md (gitignored, personal)
The dev-team rationale for the UI stack notes/argumentaire-stack-ui-spartan-cdk-tailwind.md (gitignored, personal)

7. To be added

This doc is intentionally not exhaustive on day one. Sections that will land as the project grows:

  • Local infra recipe — Docker Compose for Postgres + Redis + (later) OTel Collector.
  • Auth dev-loop — once the Microsoft 365 Developer tenant is provisioned, the workflow for testing OIDC locally.
  • Component patterns — the in-house spartan-style components (CDK + Tailwind) as they ship, with a11y notes per component.
  • Debugging tips — Angular DevTools, NestJS inspector, Prisma query log, OTel trace inspection.
  • Release workflow — once the on-prem deploy ADR lands.
  • Renovate / dep update policy — when Renovate is configured.