# SSR and Caching Server-side rendering via goja (Go JS runtime) with HTML caching. For the full workflow, prefer the `tibi-ssr-caching` skill. ## Related skills - `frontend-architecture` when SSR changes interact with route loading, i18n, or `App.svelte` data flow. - `media-seo-publishing` when SSR output includes image URLs, SEO metadata, or publication timing. - `tibi-hook-authoring` when SSR changes depend on neighboring hook behavior such as public filtering or cache invalidation. ## Request flow 1. `get_read.js` receives the request and calls `lib/ssr-server.js`. 2. `get_read.js` loads `lib/app.server.js` and calls `app.default.render({ url })`. 3. `frontend/src/ssr.ts` stays thin and only initializes locale state before rendering `App.svelte`. 4. `frontend/src/App.svelte` is responsible for actual page data loading for both browser and SSR. 5. During SSR, `App.svelte` calls the same `loadContent(...)` path directly inside `typeof window === "undefined"`. 6. Rendered HTML is stored in the `ssr` collection together with dependency tracking strings. Keep browser and SSR content loading on the same application path whenever possible. Do not fork a second data-loading model for SSR unless the current architecture explicitly requires it. ## Build - SSR bundle is built via `yarn build:server` and outputs to `lib/app.server.js`. - The project no longer uses Babel for SSR. - goja-compatible transforms are configured in `esbuild.config.server.js` via `supported`. - The server build must remove frontend-only splitting/outdir options inherited from the shared esbuild config. ## Cache invalidation - `clear_cache.js` hook invalidates SSR cache entries based on collection dependencies. - Dependencies are stored as strings like `content:` or `content:*`. - `DELETE` invalidation must be robust even when `context.data.id` is missing. ## Route validation - SSR route validation is active in `config.js`. - Public page URLs are language-prefixed (`/de/...`, `/en/...`), while `content.path` in the DB is stored without that prefix. - `ssrValidatePath()` must strip the language prefix before querying content and return a canonical language-prefixed URL when needed. - If route validation changes, inspect `api/hooks/config.js`, `filter_public.js`, and the frontend route-loading path together instead of patching only one side. ## 404 signaling The SSR hook (`get_read.js`) checks `context.is404` after rendering to determine the HTTP status code. If `true`, it returns HTTP 404 with the rendered 404 page HTML (and does not cache the result). `NotFound.svelte` sets `context.is404 = true` during SSR. When the component renders (only when the page is not found), its top-level script sets the flag. The goja runtime provides `context` as a global during SSR, so it's available from the compiled frontend code. When adding a 404 page or changing the not-found logic, ensure `context.is404` is still set during SSR. ## Related validation - After SSR changes, run `yarn build:server` plus the narrowest reachable SSR/public-read check. - If caching or publication behavior changed, also rerun the relevant mutation-side invalidation check instead of relying on HTML diffing alone.