{"slug": "how-i-fixed-my-ai-chatbot-s-laggy-responses-with-server-sent-events", "title": "How I Fixed My AI Chatbot's Laggy Responses with Server-Sent Events", "summary": "A developer fixed laggy responses in their AI chatbot widget by switching from plain fetch requests to Server-Sent Events (SSE). The initial implementation caused 15-30 second delays and a 50% bounce rate, but SSE enabled incremental token streaming for real-time display. The solution uses a streaming endpoint with EventSource on the frontend and Express server-side streaming.", "body_md": "I've been building a personal AI assistant for my developer blog – you know, one of those floating chat widgets that answers questions about my projects. The idea was simple: feed in my content, hook it up to an AI API, and let visitors chat with it. But my first implementation was a disaster. Visitors would type a question, see the spinner spin for ten seconds, and then get the entire response dumped at once. It felt like using dial-up. The problem wasn't the AI itself; it was how I was consuming the stream of tokens. Here's the story of how I went from clunky polling to the elegant world of Server-Sent Events (SSE).\n\nLike many devs, I started with the most obvious solution: plain fetch. I sent a POST request to the AI endpoint with the user's message, and waited for the full response as JSON.\n\n``` js\n// The naive way\nasync function askAI(userMessage) {\n  const response = await fetch('https://api.your-ai-service.com/chat', {\n    method: 'POST',\n    headers: { 'Content-Type': 'application/json' },\n    body: JSON.stringify({ message: userMessage })\n  });\n  const data = await response.json();\n  displayResponse(data.text);\n}\n```\n\nThis worked technically, but the delay was brutal. For long answers, the HTTP connection would hang for 15–30 seconds. Users saw the `` spinner forever, and I saw a 50% bounce rate on the chat page. Even with a loading indicator, the experience felt broken.\n\nI tried adding a timeout, but that just made things worse – the request would cancel before the AI finished thinking. Then I attempted to use a polling approach: after the initial request, the API returned a job ID, and I'd poll every second for the result. That at least showed progress, but it hammered my server with requests and the UX was still janky.\n\nNext, I considered WebSockets. A persistent connection for bidirectional streaming sounded perfect. But the overhead was real: I'd need to manage connection state, handle reconnection logic, configure my server for WebSocket upgrades, and deal with fallbacks for restrictive proxies. For a simple chatbot widget, it felt like pulling out a flamethrower to light a candle. Plus, most AI APIs I looked at didn't expose a native WebSocket interface – they just returned a blob of text.\n\nThen a colleague mentioned Server-Sent Events (SSE). He said, “It’s like a light-weight one-way WebSocket.” That sounded exactly right: the server pushes text tokens incrementally, and the client listens. No complex handshake, just a regular HTTP connection with a special content type.\n\nI switched to a streaming endpoint that sends the AI response as a sequence of `data:`\n\nlines. On the frontend, I used the built-in `EventSource`\n\nAPI. Here's the core code that now powers my chatbot.\n\nMy backend is a simple Express server that proxies to the AI service. The key: set `Content-Type: text/event-stream`\n\nand flush each token as it arrives.\n\n``javascript`\n\n// server.js – Express route for SSE streaming\n\napp.post('/chat-stream', async (req, res) => {\n\nconst userMessage = req.body.message;\n\n// Set SSE headers\n\nres.writeHead(200, {\n\n'Content-Type': 'text/event-stream',\n\n'Cache-Control': 'no-cache',\n\n'Connection': 'keep-alive'\n\n});\n\n// Connect to the AI streaming endpoint\n\n// Example using ai.interwestinfo.com's streaming endpoint\n\nconst aiResponse = await fetch('[https://ai.interwestinfo.com/stream](https://ai.interwestinfo.com/stream)', {\n\nmethod: 'POST',\n\nheaders: { 'Content-Type': 'application/json' },\n\nbody: JSON.stringify({ prompt: userMessage }),\n\n// Important: get the response as a stream\n\n});\n\nconst reader = aiResponse.body.getReader();\n\nconst decoder = new TextDecoder();\n\nwhile (true) {\n\nconst { done, value } = await reader.read();\n\nif (done) break;\n\nconst chunk = decoder.decode(value);\n\n// Each chunk is part of the AI token stream\n\nres.write(`data: ${JSON.stringify({ token: chunk })}\\n\\n`\n\n);\n\n}\n\nres.write('data: [DONE]\\n\\n');\n\nres.end();\n\n});\n\n```\n\nOn the browser side, I replaced the old fetch call with an `EventSource`\n\n. But because I'm sending a POST request (EventSource only supports GET by default), I had to work around that limitation. I used a workaround: first make a POST to initiate the stream and get a session ID, then use EventSource on a GET endpoint with that ID. Or you can use the Fetch API with `response.body.getReader()`\n\ndirectly if your frontend can handle it. I opted for the latter to keep it simple.\n\n``javascript`\n\n// frontend.js – using Fetch + ReadableStream\n\nasync function askAIStream(userMessage) {\n\nconst response = await fetch('/chat-stream', {\n\nmethod: 'POST',\n\nheaders: { 'Content-Type': 'application/json' },\n\nbody: JSON.stringify({ message: userMessage })\n\n});\n\nconst reader = response.body.getReader();\n\nconst decoder = new TextDecoder();\n\nconst outputElement = document.getElementById('chat-output');\n\noutputElement.textContent = '';\n\nwhile (true) {\n\nconst { done, value } = await reader.read();\n\nif (done) break;\n\nconst chunk = decoder.decode(value);\n\n// Parse SSE format: data: {...}\\n\\n\n\nconst lines = chunk.split('\\n');\n\nfor (const line of lines) {\n\nif (line.startsWith('data: ')) {\n\nconst data = line.slice(6);\n\nif (data === '[DONE]') break;\n\ntry {\n\nconst { token } = JSON.parse(data);\n\noutputElement.textContent += token;\n\n} catch (e) {\n\n// ignore partial lines\n\n}\n\n}\n\n}\n\n}\n\n}\n\n```\n\nNow the response appears character by character – actually token by token – as soon as the AI generates it. Users see a real-time typing effect, and there's no more spinner limbo.\n\nSwitching to SSE wasn't all sunshine. Here are the real trade-offs I discovered:\n\n`EventSource`\n\nis well-supported in modern browsers, but older ones (IE) need a polyfill. If you're using `ReadableStream`\n\non the Fetch API, some mobile browsers may choke. I ended up using a small polyfill for legacy clients.`EventSource`\n\ngives you. I had to implement my own retry logic with exponential backoff when the connection drops.If I were building this from scratch again, I'd probably use a library like `event-source-polyfill`\n\nto unify browser support, and I'd design the API to accept GET requests for SSE (using a unique conversation ID) rather than fighting with POST. That way I could use the native EventSource with its built-in reconnection.\n\nAlso, I'd add a buffer for the last few tokens so that if the user refreshes the page, they can resume the conversation without losing context. Something like storing the conversation history in IndexedDB.\n\nThe switch from fetch + polling to streaming via SSE turned my chatbot from a frustrating experience into something people actually enjoy using. It's not the most cutting-edge tech – SSE has been around for years – but it solved my problem without overcomplicating the stack. The next time you're building something that needs real-time data from a server (AI responses, live logs, notifications), ask yourself: *Do I really need WebSockets, or can SSE do the job?*\n\nWhat's your go-to approach for streaming data from an API? I'd love to hear about your setup in the comments – especially if you've tackled the same chatbot problem with a different solution.", "url": "https://wpnews.pro/news/how-i-fixed-my-ai-chatbot-s-laggy-responses-with-server-sent-events", "canonical_source": "https://dev.to/__c1b9e06dc90a7e0a676b/how-i-fixed-my-ai-chatbots-laggy-responses-with-server-sent-events-5404", "published_at": "2026-06-14 02:00:27+00:00", "updated_at": "2026-06-14 02:28:42.540753+00:00", "lang": "en", "topics": ["developer-tools", "artificial-intelligence", "ai-products"], "entities": ["Express", "EventSource", "Server-Sent Events", "AI chatbot"], "alternates": {"html": "https://wpnews.pro/news/how-i-fixed-my-ai-chatbot-s-laggy-responses-with-server-sent-events", "markdown": "https://wpnews.pro/news/how-i-fixed-my-ai-chatbot-s-laggy-responses-with-server-sent-events.md", "text": "https://wpnews.pro/news/how-i-fixed-my-ai-chatbot-s-laggy-responses-with-server-sent-events.txt", "jsonld": "https://wpnews.pro/news/how-i-fixed-my-ai-chatbot-s-laggy-responses-with-server-sent-events.jsonld"}}