When a "Live" Site Requires Live Code
\nImagine: you're writing a messenger in TypeScript. A user sends a message — and the recipient should see it instantly, without refreshing the page. Or a stock exchange dashboard where stock prices change every second. Or simply a notification feed that updates itself. How to implement this? Two main candidates are long polling and WebSocket. But which one to choose to avoid ruining performance and breaking UX?
\nIn this article, we will use real TypeScript examples to break down how each technology works, where they shine, and where they fall short. You'll get not only theory but also ready-made code snippets for Node.js and the browser. Let's go!
\n\nWhat is Long Polling in TypeScript and How Does It Work
\nLong polling is an evolution of a regular HTTP request. The client sends a request to the server, but the server does not respond immediately if there is no data. It keeps the connection open until a new event occurs. As soon as the data is ready — the server sends a response, the client processes it and immediately creates a new request. The cycle repeats.
\nSounds like a workaround? Partially, yes. But it works even through the strictest corporate proxies that block WebSocket.
\n\nImplementing Long Polling in TypeScript (Server Side)
\nLet's write a simple server using Express with TypeScript. It will emulate a message queue.
\nimport express, { Request, Response } from 'express';\nimport cors from 'cors';\n\nconst app = express();\napp.use(cors());\napp.use(express.json());\n\n// Message storage (queue simulation)\nconst messages: string[] = [];\n\n// Endpoint for long polling\napp.get('/poll', (req: Request, res: Response) => {\n // Wait for a new message for up to 30 seconds\n const checkForMessage = () => {\n if (messages.length > 0) {\n // Send the first message and remove it from the queue\n const message = messages.shift();\n res.json({ data: message, timestamp: Date.now() });\n } else {\n // If no messages — keep waiting\n setTimeout(checkForMessage, 1000); // check every second\n }\n };\n\n // Timeout in case there are no messages for a long time\n const timeout = setTimeout(() => {\n res.json({ data: null, timeout: true });\n }, 30000);\n\n // If the client disconnects — clear timers\n req.on('close', () => {\n clearTimeout(timeout);\n });\n\n checkForMessage();\n});\n\n// Endpoint for sending a message\napp.post('/send', (req: Request, res: Response) => {\n const { message } = req.body;\n if (message) {\n messages.push(message);\n res.status(200).json({ status: 'ok' });\n } else {\n res.status(400).json({ error: 'message is required' });\n }\n});\n\napp.listen(3000, () => console.log('Long poll server on port 3000'));\n\nClient Side in TypeScript (Browser)
\nasync function startLongPolling() {\n try {\n const response = await fetch('http://localhost:3000/poll');\n const data = await response.json();\n\n if (data.data) {\n console.log('New message:', data.data);\n // Update UI\n updateUI(data.data);\n }\n\n // Immediately start a new poll\n startLongPolling();\n } catch (error) {\n console.error('Long polling error:', error);\n // Wait 2 seconds and try again\n setTimeout(startLongPolling, 2000);\n }\n}\n\nfunction updateUI(message: string) {\n // Your code for updating the DOM\n const container = document.getElementById('messages');\n if (container) {\n const item = document.createElement('li');\n item.textContent = message;\n container.appendChild(item);\n }\n}\n\n// Start on page load\ndocument.addEventListener('DOMContentLoaded', startLongPolling);\nConclusion: long polling is simple to implement and does not require special protocols. But each request involves HTTP headers, cookies, and overhead. With 1000 simultaneous clients