Proxy /api/v1 and /api/auth from Hono to FastAPI in prod
All checks were successful
Deploy to VPS / deploy (push) Successful in 45s

In production the browser talks to the Hono server, which only proxied
/api/copilotkit/*. All other /api/* requests hit the SPA static fallback
and got index.html back. Forward /api/v1/* and /api/auth/* to BACKEND_URL.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Carlos Escalante
2026-04-29 22:43:34 -06:00
parent 6b3069eef4
commit aa4bb6512f

View File

@@ -11,6 +11,8 @@ import { Hono } from "hono";
const AGENT_URL = const AGENT_URL =
process.env.AGENT_URL ?? "http://backend:8000/api/v1/agent/agui"; process.env.AGENT_URL ?? "http://backend:8000/api/v1/agent/agui";
const BACKEND_URL =
process.env.BACKEND_URL ?? "http://localhost:8001";
const isProd = process.env.NODE_ENV === "production"; const isProd = process.env.NODE_ENV === "production";
const PORT = parseInt(process.env.PORT ?? (isProd ? "3000" : "3001")); const PORT = parseInt(process.env.PORT ?? (isProd ? "3000" : "3001"));
@@ -376,6 +378,32 @@ app.all("/api/copilotkit/*", async (c) => {
app.get("/api/health", (c) => c.json({ ok: true })); app.get("/api/health", (c) => c.json({ ok: true }));
// Proxy backend API calls (FastAPI). In dev these hit Vite's proxy directly,
// but in prod the browser talks to this Hono server, which must forward
// `/api/v1/*` and `/api/auth/*` to the FastAPI container — otherwise the SPA
// fallback below swallows them and returns index.html.
const proxyToBackend = async (c: import("hono").Context) => {
const url = new URL(c.req.url);
const target = `${BACKEND_URL}${url.pathname}${url.search}`;
const method = c.req.method;
const headers = new Headers(c.req.raw.headers);
headers.delete("host");
const init: RequestInit = {
method,
headers,
redirect: "manual",
};
if (method !== "GET" && method !== "HEAD") {
init.body = c.req.raw.body;
// @ts-expect-error undici requires duplex for streamed bodies
init.duplex = "half";
}
return fetch(target, init);
};
app.all("/api/v1/*", proxyToBackend);
app.all("/api/auth/*", proxyToBackend);
// In production, serve the Vite build output. // In production, serve the Vite build output.
if (isProd) { if (isProd) {
app.use("/*", serveStatic({ root: "./dist" })); app.use("/*", serveStatic({ root: "./dist" }));