The server runtime/API proxying

API proxying

How server routes are intercepted and forwarded.

Interception

In development, bionicjs dev exposes one public origin: localhost:3000. Any request starting with /api is intercepted by the Vite dev server and forwarded to the internal Nitro server running on port 3001:

The proxy rule
1server: {
2  port: publicPort,
3  strictPort: true,
4  proxy: {
5    "/api": `http://localhost:${nitroPort}`,
6  },
7},

The developer sees only one origin, and the browser sees only one origin. Requests to /api/... never trigger CORS, never carry a different host header, and never need the frontend to know where the backend actually lives.

Request flow in development
1browser --fetch("/api/auth/login")--
2      Vite (:3000)
3      --proxy--> Nitro (:3001)
4      --handler--> h3 handler
5      --rpc--> Python (optional)

Dev vs production

The application code never needs to know whether it is running in dev or prod - it simply fetches /api. The difference is only in the routing layer:

ConcernDevelopmentProduction
API servingVite proxy to NitroNitro serves /api directly
Static assetsServed by Vite with HMRServed by Nitro from public/
Unknown routesVite SPA fallbackNitro SPA fallback
OriginsOne public origin (:3000)One origin (your domain)

In production, the Nitro server itself handles the /api routes natively, while serving static files and the React application fallback for everything else. There is no proxy, no second process, and no extra hop - the same artifact that serves your HTML also serves your API.

ⓘ
Because the client never hardcodes a backend host, you can move where the API runs - local dev, a VPS, an edge worker - without touching frontend code. The proxy layer is configuration, not application logic.