How to build a chat app with WebSockets in Node.js
Real-time chat applications require bidirectional communication where the server can push messages to clients instantly without polling.
With over 12 years of Node.js development experience since 2014 and as the creator of CoreUI, I’ve built numerous real-time applications with WebSockets.
WebSockets provide full-duplex communication channels over a single TCP connection, perfect for chat, notifications, and live updates.
The native ws library offers a lightweight WebSocket implementation for Node.js without the overhead of frameworks.
Use the ws library to create a WebSocket server that broadcasts messages to all connected clients.
npm install ws
Create WebSocket chat server:
const WebSocket = require('ws')
const http = require('http')
const server = http.createServer()
const wss = new WebSocket.Server({ server })
const clients = new Set()
wss.on('connection', (ws) => {
console.log('New client connected')
clients.add(ws)
// Send welcome message
ws.send(JSON.stringify({
type: 'system',
message: 'Welcome to the chat!',
timestamp: new Date().toISOString()
}))
// Broadcast user joined
broadcast({
type: 'system',
message: 'A user joined the chat',
timestamp: new Date().toISOString()
}, ws)
// Handle incoming messages
ws.on('message', (data) => {
try {
const message = JSON.parse(data)
// Broadcast to all clients
broadcast({
type: 'message',
user: message.user || 'Anonymous',
text: message.text,
timestamp: new Date().toISOString()
})
} catch (error) {
console.error('Error parsing message:', error)
}
})
// Handle disconnect
ws.on('close', () => {
console.log('Client disconnected')
clients.delete(ws)
broadcast({
type: 'system',
message: 'A user left the chat',
timestamp: new Date().toISOString()
})
})
ws.on('error', (error) => {
console.error('WebSocket error:', error)
clients.delete(ws)
})
})
function broadcast(data, exclude = null) {
const message = JSON.stringify(data)
clients.forEach(client => {
if (client !== exclude && client.readyState === WebSocket.OPEN) {
client.send(message)
}
})
}
server.listen(3000, () => {
console.log('Chat server running on port 3000')
})
Client-side JavaScript:
const ws = new WebSocket('ws://localhost:3000')
ws.onopen = () => {
console.log('Connected to chat server')
}
ws.onmessage = (event) => {
const data = JSON.parse(event.data)
displayMessage(data)
}
function sendMessage(text) {
ws.send(JSON.stringify({
user: 'John',
text: text
}))
}
function displayMessage(data) {
console.log(`[${data.timestamp}] ${data.user}: ${data.text}`)
}
Best Practice Note
Always validate and sanitize incoming messages to prevent XSS attacks. Track connected clients in a Set for efficient broadcasts. Check readyState === WebSocket.OPEN before sending to avoid errors. Implement heartbeat/ping-pong to detect dead connections. For production, add user authentication, message persistence, and rate limiting. Consider Socket.IO for broader browser support and automatic fallbacks. This is how we build real-time features in CoreUI backends—using native WebSockets for performance with proper error handling and connection management.



