feat: proxy the BFF to Vite for single-origin HMR in one terminal window - #99
Conversation
Signed-off-by: Marek Dano <mk.dano@gmail.com>
Resolve README.md conflict in the Available Scripts table: keep this branch's updated script descriptions (dev/dev:all reflecting the Vite proxy) and add main's secrets:scan/secrets:audit rows.
072ff80 to
0d5b004
Compare
a-effort
left a comment
There was a problem hiding this comment.
Reviewed the diff. One blocker.
The vite-dev-proxy plugin registers a wildcard route with prefix "/" and routes ["/"], then relies on find-my-way preferring statically registered routes over this catch-all. That assumption holds for routes registered before the plugin, but the comment in the plugin file acknowledges that bare GET / is excluded from routes because routes/app.ts owns it. The problem: @fastify/http-proxy also registers a GET / by default when prefix is "/", which would collide with routes/app.ts's GET / as a duplicate route. The exclusion note says this is intentional, but the mechanism that prevents the collision is not obvious. If @fastify/http-proxy's internal route registration includes GET / regardless of the routes array, this will throw a duplicate route error at startup. Before merging, confirm that the plugin does not register GET / when routes: ["/"] is specified, or add an explicit test that the BFF starts without error when VITE_DEV_SERVER_URL is set.
A second concern: the predev:all hook runs npm run generate before starting either dev server, but generate runs orval which requires openapi.json to be present and valid. If a contributor clones the repo and openapi.json is absent or stale (e.g., after a rebase), generate will fail before anything starts. The README documents this as expected, but the error message from orval is not especially clear. A guard that checks for openapi.json before running generate, with a pointer to npm run openapi:refresh, would reduce friction.
|
|
||
| export default async function viteDevProxyPlugin(fastify: FastifyInstance): Promise<void> { | ||
| await fastify.register(httpProxy, { | ||
| upstream: config.viteDevServerUrl!, |
There was a problem hiding this comment.
Small one: the ! here relies on index.ts registering it to guarantee this is set. It may be worth a runtime check too, since @fastify/http-proxy's own validation won't catch an undefined upstream in this config. If the registration is ever removed, this would fail silently at startup and only surface as an error on the first proxied request.
Suggested fix:
if (!config.viteDevServerUrl) {
throw new Error("viteDevProxyPlugin registered without VITE_DEV_SERVER_URL set");
}
await fastify.register(httpProxy, {
upstream: config.viteDevServerUrl,
...Cheap safeguard, fails fast and surfaces an error message. :)
- Guard predev:all with a clear error when openapi.json is missing,
instead of letting orval fail with a less helpful message
- Add a regression test asserting the BFF starts without a duplicate
GET / route when both vite-dev-proxy and app routes are registered
- Throw explicitly if viteDevProxyPlugin is registered without
VITE_DEV_SERVER_URL set, rather than relying on a `!` assertion
Signed-off-by: Marek Dano <mk.dano@gmail.com>
Signed-off-by: Marek Dano <mk.dano@gmail.com>
|
@a-effort - please review again |
Summary
The BFF and frontend used to each need their own terminal (
npm run dev in server/, npm run devat root).npm run dev:allnow starts both together with one command — down to a single terminal for those two, with the API inmcp-context-forgeremaining the only other one needed.build:watch, no HMR) or visiting Vite's dev server directly on:5173— which needs a separate
/api/*route to the BFF and 403s on login/SSE/password-reset unlessPUBLIC_ORIGINis manually overridden to work around the BFF's origin-guard.VITE_DEV_SERVER_URLis set, via a newvite-dev-proxyplugin (@fastify/http-proxy) registered in place of the static-file plugin./api/*,/auth/*,/healthz, and the auth-aware/redirect stay BFF-owned and unaffected — verified via manual route-precedence checks.:3000), matching how the app is served in production, so there's no origin mismatch to configure around.npm run dev:bffandnpm run dev:all(viaconcurrently) to start the BFF (withVITE_DEV_SERVER_URLset) and the Vite dev server together in one command.predev/predev:bff/predev:allnpm lifecycle hooks that checknode_modulesis in sync beforedev/dev:bff/dev:allrun, so a missing install fails with a clear "run npm install" message instead of a barecommand not found.predev:allalso runsnpm run generatebefore starting either dev server, sodev:allalways has freshsrc/generated/types — both on a fresh clone (that directory is gitignored and 34 files import from it) and after editingopenapi.jsonmid-session..env.exampleto document the new flow, alongside the existingbuild/build:watchpath for testing the exact BFF-served bundle.Test plan
npm run build— succeedsnpm run lint(inserver/) — typechecks cleannpm run dev:allfrom repo root:http://localhost:3000/loads, redirects to/app/loginwhen unauthed, HMR client/React-refresh correctly injected into/app/curl http://localhost:3000/healthzand/api/*still return BFF responses (not proxied to Vite)node_modules—dev,dev:bff, anddev:alleach fail with the intended message instead ofcommand not foundsrc/generated/—dev:allregenerates it viapredev:allbefore either dev server starts