Files
PersonalBlog/docs/adr/ADR-008-react-vite-admin.md
T
implementer edbf8cb7ea
CI / Stage 1 — Frozen lockfile install (E00-S05-T01) (pull_request) Successful in 44s
CI / Stage 6 — PostgreSQL integration tests (E00-S05-T01) (pull_request) Successful in 1m8s
CI / Stage 4 — Unit tests (E00-S05-T01) (pull_request) Successful in 1m40s
CI / Stage 7 — Build the admin and server applications (E00-S05-T01) (pull_request) Successful in 1m5s
CI / Stage 2 — Typecheck (E00-S05-T01) (pull_request) Successful in 1m8s
CI / Stage 5 — Architecture tests (E00-S05-T01) (pull_request) Successful in 3m7s
CI / Stage 3 — Formatting/lint policy (E00-S05-T01) (pull_request) Successful in 45s
docs(adr): record React/Vite admin decision as ADR-008
Commits ADR-008 (React/Vite admin) for [E01-S01-T07], with the six mandated
sections (Context, Decision, Alternatives, Consequences, Operational impact,
Revisit trigger) per ADR index 46, and adds docs/adr/README.md as the
repository copy of the ADR index (section 70) so the decision-text match is
verifiable inside the repo. Documentation-only: no source, manifest,
lockfile, workflow or test changes.
2026-08-31 00:56:54 +00:00

7.8 KiB

ADR-008: React/Vite admin

  • Status: Accepted
  • Date: 2026-08-31
  • Deciders: platform stream
  • References: ADR index (section 70, docs/adr/README.md), ADR-001, ADR-007, Architecture wiki (sections 1, 4, 9, 11, 20, 42), Technology-Stack wiki (sections 5.2, 5.3, 6, 7, 8)

Context

EPPP is a modular monolith (ADR-001) whose single application image contains the public server, admin API, rendering pipeline, extension runtime, core services, built admin assets and first-party extensions (Architecture §4). The v0.1 scope explicitly includes an "administration interface" (§1), and Architecture §20 already fixes its shape: a React client app built with Vite, served by the same image, with /admin/* talking to the admin API under /api/admin/v1/* and an initial IA of Dashboard, Content→Posts, Home, Navigation, Appearance (active theme, theme settings), Extensions and Settings→Site. ADR-001 fixes the runtime as "Node.js 24 LTS with Fastify 5, PostgreSQL 18, React 19 for server-rendered public components and a Vite admin".

React and Vite are already the pinned toolchain in the golden compatibility tuple. React / React DOM 19.2.8 (class C, exact-pinned) is in the runtime stack and golden tuple (Technology-Stack §5.2, §7), and the build/admin/test toolchain is pinned to Vite 8.2.2, @vitejs/plugin-react 6.1.0, pnpm 11.23.0, Vitest 4.1.10 and Playwright 1.62.1 (§5.3). The LTS strategy keeps React exact-pinned at 19.2.8, keeps React Server Components out of v1 and keeps React out of persisted content formats (§6); direct platform dependencies are exact-pinned with a committed lockfile (§8).

The workspace does not implement the admin app yet — apps/server is the bare Fastify health shell and there is no apps/admin (Architecture §9; the CI build-apps stage already globs apps/** so the admin is picked up when it lands). This ADR therefore records the decision ahead of the code that implements it, formalising what the Architecture and Technology-Stack wikis already mandate: the admin frontend is a React client app built with Vite and served by the same application image.

Decision

EPPP builds the admin frontend with React/Vite admin: a React client application built with Vite — React 19.2.8 and Vite 8.2.2, exact-pinned class C dependencies from the golden compatibility tuple (Technology-Stack §5.2, §5.3, §7) — served by the same application image (ADR-001, Architecture §4). The admin lives under /admin/* and calls the admin API at /api/admin/v1/* (§20, §42). It is deliberately a separate concern from the public rendering pipeline: public pages stay server-rendered with no client hydration (ADR-007, §19.1), while the admin is a client-rendered SPA — the interactive management surface where JavaScript is expected and required. React Server Components stay out of v1 and React stays out of persisted content formats (Technology-Stack §6). The decision text — React/Vite admin — matches the ADR index entry (ADR-008, section 70).

Alternatives

  • Server-rendered admin through the public React SSR pipeline — rejected: the admin is an interactive management surface where JavaScript is expected; routing it through the zero-JavaScript, non-hydrated SSR pipeline (ADR-007, §19.1) would buy nothing — every admin page needs client interactivity — and would couple admin rendering to the public pipeline's per-request server rendering.
  • Separate admin framework (e.g. Next.js, Angular, Vue SPA) — rejected: the golden tuple pins React 19.2.8 (Technology-Stack §5.2, §7) and ADR-001 already records "a Vite admin"; a second frontend ecosystem would split the admin UI from the public components with no v1 requirement justifying it.
  • Framework-less Vite / hand-rolled DOM — rejected: the admin IA (§20) needs a component model for the content, theme, extension and settings screens; re-implementing state management and composition by hand duplicates what React already provides, and the golden tuple already pins React.
  • Build-time static-site-generated admin — rejected: the admin is an authenticated, connection-backed interface over the canonical database (ADR-005, ADR-019); build-time HTML would go stale against the canonical DB and add a rebuild/redeploy cycle per content change.
  • Admin as a separate service or image — rejected: ADR-001 fixes one application image containing built admin assets; a separate admin deployment would break the single-deployable-unit model and add service boundaries with no v1 benefit.

Consequences

  • Positive: one frontend technology — React 19.2.8 — spans the public SSR pipeline (ADR-007) and the admin SPA, so patterns, components and the golden tuple pin are shared; Vite gives fast HMR during admin development and a static build that ships inside the same image (ADR-001); the admin is explicitly outside the public zero-JavaScript baseline (§19.1), so its interactivity does not conflict with the public surface; the exact-pinned toolchain (Vite 8.2.2, React 19.2.8, committed lockfile) keeps admin builds reproducible under the ADR-026 upgrade lanes.
  • Negative: the admin ships client JavaScript and a client runtime to authenticated admin users; a client-side build step joins the CI pipeline (the build-apps stage globs apps/**) and brings its own test tooling (Vitest, Playwright — Technology-Stack §5.3, §7); two rendering paths — public SSR and admin SPA — must be maintained, and the client/server contract at /api/admin/v1/* must stay versioned; the admin is an authenticated security surface (opaque DB-backed sessions, ADR-019) that the security baseline must keep covering (CSRF, session handling, authz).
  • Neutral: Vite is a build-time detail — what ships is static assets served behind /admin/* from the same image; the decision constrains the later client-side decisions (zero-JS public baseline and the client-island model, ADR-009 and ADR-010) to the public surface, not the admin.

Operational impact

  • apps/admin builds with Vite into static assets that are served by the same application image behind /admin/* (Architecture §20); there is no separate admin service, container or deployment (ADR-001).
  • /admin/* serves the SPA shell; the SPA reads and writes through the admin API at /api/admin/v1/* (§42), secured by the Fastify lifecycle and opaque DB-backed sessions (ADR-019).
  • The admin build runs in the CI build-apps stage — the pnpm apps/** glob picks apps/admin up automatically when the app lands — and admin behaviour is covered by Vitest unit suites and Playwright e2e (Technology-Stack §5.3, §7).
  • Admin traffic is authenticated and internal; the admin is not part of the public zero-JavaScript surface (§19.1) — client JavaScript is expected and required there — so no byte-budget applies to admin assets.
  • The admin is not implemented yet — today rollback is reverting this documentation commit; once the admin lands, rollback is redeploying the previous image (built admin assets live inside the image, so there is no separate asset deploy to coordinate).

Revisit trigger

  • Revisit when the admin grows a requirement the React/Vite SPA model cannot meet — for example a genuinely different client architecture or offline capability — with evidence per the architecture review gates (ADR index section 68).
  • Revisit when the golden tuple moves React or Vite to a new major — an ADR-recorded upgrade program item (Technology-Stack §7/§8, ADR-026 lane) — or changes the React pin's support class.
  • Revisit when the public client-side decisions (ADR-009, ADR-010) change the boundary between the public surface and the admin, or the admin API contract at /api/admin/v1/* needs a different shape.
  • Revisit when the single-image model (ADR-001) changes such that built admin assets must be served or deployed separately.