The SSE Engineering Inflection Point: Why Your Frontend Needs a BFF Layer
API keys baked into frontend bundles are trivially extractable; a BFF layer is the smallest practical step that closes that leak without a full backend rewrite. It also gives frontend teams a server-side surface they control, which is increasingly necessary as AI features move from toy demos to products that handle real credentials and streaming data.
A direct frontend-to-LLM streaming setup works for a demo but leaks API keys into the client bundle, buries ReadableStream and SSE parsing inside Vue components, and hits same-origin policy walls. Inserting a thin Node.js BFF layer between the browser and the LLM API moves the key to a server-side process, offloads buffer management and stream decoding from the UI, and pairs with a Vite dev-server proxy to make cross-origin requests invisible to the browser.
The BFF is a single Express file that accepts a prompt from the frontend, attaches the secret key, calls the DeepSeek API with `stream: true`, and pipes the response back. A Vite proxy rewrites `/api/*` paths to the BFF's port, so the browser sees only same-origin requests. The result is a cleaner frontend that consumes data without touching binary streams, and a security boundary that keeps credentials out of the JavaScript bundle.
This architecture is a deliberate step in an evolution: first understand raw SSE and buffer mechanics, then wrap them in a server-side layer that frontend engineers own and maintain. The next stage completes the pattern by parsing the SSE stream inside the BFF so the frontend receives only plain text.
SSE is a better fit than WebSocket for LLM streaming because the interaction is inherently one-way: the frontend sends one prompt and the server pushes tokens back; full-duplex adds handshake and heartbeat overhead with no benefit.
The BFF pattern originated from frontend engineers writing their own Node services when backend teams couldn't deliver API changes fast enough — it's an organizational workaround that became an architectural pattern.
Using native `fetch` in Node.js instead of axios or node-fetch eliminates a dependency and keeps the BFF's attack surface small, which matters when the service handles API keys.
The `dotenv.config({ path: ['.env.local', '.env'] })` array syntax creates a priority loading order that mirrors Vite's own env-file resolution, keeping local overrides consistent across frontend and BFF.
The article's code is a deliberate midpoint: it proves the BFF forwarding chain works before adding stream parsing logic, following a layer-by-layer verification strategy rather than building everything at once.