Files
familienarchiv/docs/TODO-frontend.md
Marcel 09ec2103c8 fix: correct malformed @Value annotations in DataInitializer
Missing closing braces caused Spring to inject the literal placeholder
string instead of resolving the property, silently ignoring any
app.admin.username / app.admin.password env-var overrides.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 12:16:00 +01:00

6.3 KiB
Raw Blame History

Frontend TODO

Findings from architectural review. Ordered roughly by severity.


Bugs

Backend URL hardcoded as localhost in the session hook

File: src/hooks.server.ts:20

const response = await fetch('http://localhost:8080/api/users/me', {

The userGroup handle (which runs on every request) calls the backend via a hardcoded localhost:8080 URL. The API_INTERNAL_URL env var used in handleFetch is not applied here. Inside Docker, localhost from the frontend container does not resolve to the backend container — requests will fail silently (the catch swallows the error) and event.locals.user will be undefined for every request.

Fix: Centralise the base URL:

const API_BASE = env.API_INTERNAL_URL ?? 'http://localhost:8080';
// then use it in both userGroup and handleFetch

import { env } from 'process' bypasses SvelteKit's env system

File: src/hooks.server.ts:4

import { env } from 'process';

This is a raw Node.js import that bypasses SvelteKit's $env/dynamic/private and $env/static/private modules. It won't work if the adapter is ever changed (e.g., to Deno or a serverless edge runtime), and it skips SvelteKit's build-time validation that env vars are actually set.

Fix:

import { env } from '$env/dynamic/private';

Design Issues

Every page load hits the backend twice minimum

File: src/hooks.server.ts:1534

The userGroup hook calls GET /api/users/me on every single server-side request to check the session. Then each page's +page.server.ts load function makes its own API calls. For a search page that's three sequential backend round-trips before the user sees anything.

Fix options:

  • Cache the user in the session (if Spring Session is adopted on the backend) — validate the session cookie once, not per request
  • If sticking with Basic Auth: trust the cookie value directly and only call /api/users/me when the user object is actually needed (e.g., in +layout.server.ts), not unconditionally in the hook

Two conflicting package managers

Files: package-lock.json (npm), yarn.lock (Yarn)

Both lock files exist, meaning the project has been installed with both npm and Yarn at different times. This leads to divergent dependency trees and confusing contributor setup.

Fix: Pick one (npm is already the default in the devcontainer), delete the other lock file, and add an .npmrc or package.json engines field to enforce it:

"engines": { "npm": ">=10" }

i18n is a stub — only German is implemented

Files: messages/en.json, messages/es.json

English and Spanish message files contain only the scaffolding example key hello_world. All actual UI strings are rendered directly in German inside Svelte components and are not extracted into the message catalogue.

Fix options:

  • If multi-language support is a real requirement: extract all German strings from .svelte files into messages/de.json and provide translations in en.json / es.json
  • If it's not a requirement: remove Paraglide, the messages/ directory, and the i18n hook to reduce complexity

No Dockerfile — frontend cannot run in Docker

File: docker-compose.yml (frontend service commented out)

The frontend service in docker-compose.yml is commented out because no Dockerfile exists. The SvelteKit Node adapter is already configured, so producing a Dockerfile is straightforward.

Fix: Add frontend/Dockerfile:

FROM node:24-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:24-alpine
WORKDIR /app
COPY --from=builder /app/build ./build
COPY --from=builder /app/package.json .
EXPOSE 3000
CMD ["node", "build"]

Then uncomment and complete the frontend service in docker-compose.yml, adding API_INTERNAL_URL=http://backend:8080.


Demo routes are leftover scaffolding

Files: src/routes/demo/+page.svelte, src/routes/demo/paraglide/+page.svelte

These routes were generated by the SvelteKit + Paraglide scaffolding tool. They serve no function in the application and are publicly accessible.

Fix: Delete src/routes/demo/ entirely.


Missing Capabilities

No shared TypeScript types with the backend

The frontend manually constructs objects and parses JSON responses without any type definitions that match the backend's DTOs (DocumentUpdateDTO, GroupDTO, etc.). A breaking change in the backend API — renaming a field, changing a type — won't be caught until runtime.

Fix options (pick one):

  • Simple: Define matching TypeScript interfaces manually in src/lib/types.ts for all backend response shapes and use them in load functions
  • Robust: Add springdoc-openapi to the backend and use openapi-typescript to generate types from the OpenAPI spec as part of the build pipeline

No global error page

SvelteKit renders a default unstyled error page for unhandled errors and 404s. There is no src/routes/+error.svelte in the project.

Fix: Create src/routes/+error.svelte with the application's layout and a user-friendly message. Use SvelteKit's $page.status and $page.error stores to display appropriate messages for 404 vs 500.


No loading state during navigation

Long-running searches or slow backend responses give the user no visual feedback. SvelteKit's $navigating store is available but appears unused.

Fix: Add a global navigation progress indicator in +layout.svelte:

<script>
  import { navigating } from '$app/stores';
</script>
{#if $navigating}
  <div class="progress-bar" />
{/if}

handleFetch redirect inside a hook can cause silent failures

File: src/hooks.server.ts:46

throw redirect(302, '/login');

Throwing a redirect inside handleFetch during a server-side load that makes multiple API calls (e.g., the search page calling both /api/documents/search and /api/persons) means only the first missing-token fetch triggers the redirect — subsequent ones may behave unpredictably.

Fix: Move the auth guard to +layout.server.ts as the single authoritative redirect point, and let handleFetch simply pass through without the token (letting the backend return 401). Handle 401 responses from the backend in load functions with an explicit redirect(302, '/login').