𝗢𝗻𝗲 𝗦𝗦𝗘 𝘀𝘁𝗿𝗲𝗮𝗺, 𝘀𝗲𝘃𝗲𝗻 𝗟𝗟𝗠 𝗽𝗿𝗼𝘃𝗶𝗱𝗲𝗿𝘀
I built a Next.js app that supports seven different LLM providers.
OpenAI, Claude, Gemini, Ollama, Mistral, Groq, and Azure.
I set one strict rule: The browser must use the exact same code path for every provider.
This is hard because these APIs are not the same. They use different transport methods. They send different data shapes. Some use SSE while others use NDJSON.
If you let these differences reach your UI, your code becomes a mess of "if" statements. Every time you add a provider, your frontend grows more complex.
I solved this by creating a single contract. Every provider must emit this format to the browser:
• data: {"delta":"
The browser only needs to understand three things: delta, error, and [DONE].
Here is how I built it:
Use Async Generators I treat each provider as a generator that yields plain text. This hides the complexity of the API.
The Wrapper Pattern I created a wrapper function called createSSEStream. This wrapper manages the wire format. It also ensures the stream always ends. Even if a provider fails halfway through, the wrapper sends an error and a [DONE] signal. This prevents the client from hanging.
Grouping Similar APIs OpenAI, Mistral, Groq, and Azure all use the same dialect. I wrote one implementation for all of them. Adding a new compatible provider now takes only one line of code.
Handling Outliers Anthropic and Ollama work differently. Anthropic uses specific typed events. Ollama uses NDJSON. I wrote custom parsers for them, but they both yield text into the same wrapper. The browser never knows the difference.
Privacy and Simplicity The app uses a "Bring Your Own Key" model.
• Users paste their own API key. • The key stays in local storage. • The server acts as a pure proxy. • The key is never stored in a database.
This approach removes the need for complex auth or secret management. It makes the app easy to self-host.
The lesson is simple: Model each integration as a generator of what you actually want. Wrap it once. Let the variation live in the generators so your main logic stays clean.
Optional learning community: https://t.me/GyaanSetuAi