Repository navigation
Cold server-function requests fail registration or lose JSON and HTTP semantics #2524
Description
Please provide the environment you discovered this bug in.
Analog v3 alpha on Linux, Node 24.15.0, pnpm 10.33.0. The public reproduction uses Angular 22.1.5, Vite 8.2.2, Nitro 3.0.260903-beta, and the split analog() / angular() / nitro() integration.
Regression-only reproduction commit from the requested split of #2516. It contains the existing HTTP implementation and tests/fixtures that demonstrate the failures, on top of the separately prepared compiler, Vitest, and SSR prerequisites.
git clone https://github.com/benpsnyder/analog.git
cd analog
git checkout 0c146b877
corepack enable
pnpm install --frozen-lockfile
CI=true NX_DAEMON=false NX_NO_CLOUD=true pnpm exec nx run router:test --testFile=event-handler.spec.ts,client-transport.spec.ts --skip-nx-cache
CI=true NX_DAEMON=false NX_NO_CLOUD=true pnpm exec nx run analog-app-e2e:server-functionsUse the Node version pinned in .node-version. The two regression commands are expected to fail before the repair; run both independently.
Which area/package is the issue in?
router, platform
Description
A directly exported server function in a .server.ts module that no page imports is not registered by the split Nitro integration. The first /_analog/fn/:id call reaches the HTML renderer and receives HTML instead of the JSON function result. The reproduction starts a fresh built server, obtains readiness from its listening-socket announcement, and calls the function before any HTML request.
The request/response adapter also loses HTTP and JSON semantics. The Node socket regressions show validation and malformed-input errors returning 200 instead of 400, redirects returning 200 instead of 303, and cookie responses returning 200 instead of 204. Angular's client turns null into {} and sends strings as unquoted text, while raw string/null return values are not consistently encoded as JSON.
A concrete use case is an API-only caller invoking an echo function or redirect before anyone visits the site. The application should not need a page render to register that endpoint, and JSON null, empty strings, booleans, numbers, arrays, and objects should retain their meaning.
Expected behavior:
- Discover actual directly exported server functions, including configured additional directories, and register their handler on cold startup. Exclude load-only/unrelated server modules.
- Use the same canonical server application config and function IDs as the SSR/client transforms; reject ambiguous config files at build time.
- Preserve JSON input/output values, explicit response formats, error/redirect statuses, and separate cookies over real HTTP. Preserve Angular HTTP interceptors.
- Keep discovery functional when HTML SSR is disabled.
Please provide the exception or error you saw
The unit/socket baseline reports 12 failed, 15 passed. Representative failures:
expected '{}' to be 'null'
expected 'text' to be '"text"'
expected 200 to be 400
expected 200 to be 303
expected 200 to be 204
The cold built-server request fails its content-type assertion:
Expected: /application\/json/
Actual: text/html; charset=utf-8
Other information
This is workstream 4 from Brandon Roberts' request. The focused fix and runnable HTTP fixture are published in analogjs/analog#2529, targeting alpha. Current head c1af22de104cd43067c60de3852751c5b857ae5b passes the dedicated HTTP runtime workflow, main/application E2E, Windows, and preview CI. Its description records cold-start registration, JSON/status/cookie contracts, and necessary shared prerequisites. The failing reproduction above remains pinned; this issue stays open pending merge.
Related: analogjs/analog#2496 reports over-broad discovery in the legacy vite-plugin-nitro integration. This issue covers missing cold registration and transport semantics in the split v3 integration; it does not close the separate legacy report. Request-injector disposal, cancellation, hydration, and Query cache ownership belong to the next workstream.
I would be willing to submit a PR to fix this issue
- Yes
- No
Activity
fix(router): preserve cold server-function HTTP contracts