DEV Community

Cover image for Building a Simple Chat App with WebSocket in Node.js
10000coders
10000coders

Posted on

Building a Simple Chat App with WebSocket in Node.js

Building a Simple Chat App with WebSocket in Node.js
A step-by-step guide to creating a real-time chat application using WebSocket, Node.js, and Express.

Understanding WebSocket
What is WebSocket?

Traditional HTTP vs WebSocket
┌─────────┐     ┌─────────┐     ┌─────────┐
│ Client  │────▶│ Server  │◀────│ Client  │
└─────────┘     └─────────┘     └─────────┘
     ▲              │              ▲
     │              ▼              │
     └──────────────┴──────────────┘
Enter fullscreen mode Exit fullscreen mode

Key Characteristics
Full-duplex communication
Persistent connection
Real-time data transfer
Low latency
Bi-directional communication
Project Setup

  1. Initialize Project
mkdir chat-app
cd chat-app
npm init -y
Enter fullscreen mode Exit fullscreen mode
  1. Install Dependencies
npm install express socket.io
Enter fullscreen mode Exit fullscreen mode

  1. Basic Server Setup
// server.js
const express = require('express');
const app = express();
const http = require('http').createServer(app);
const io = require('socket.io')(http);

// Serve static files
app.use(express.static('public'));

// Socket.io connection handling
io.on('connection', (socket) => {
  console.log('A user connected');

  socket.on('disconnect', () => {
    console.log('User disconnected');
  });
});

const PORT = process.env.PORT || 3000;
http.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});
Enter fullscreen mode Exit fullscreen mode

Frontend Implementation

  1. HTML Structure
<!-- public/index.html -->
<!DOCTYPE html>
<html>
<head>
    <title>WebSocket Chat</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="chat-container">
        <div class="chat-header">
            <h2>WebSocket Chat</h2>
        </div>
        <div class="chat-messages" id="messages"></div>
        <div class="chat-input">
            <input type="text" id="message" placeholder="Type a message...">
            <button onclick="sendMessage()">Send</button>
        </div>
    </div>
    <script src="/socket.io/socket.io.js"></script>
    <script src="app.js"></script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode
  1. CSS Styling
/* public/style.css */
.chat-container {
    max-width: 600px;
    margin: 0 auto;
    padding: 20px;
}

.chat-messages {
    height: 400px;
    border: 1px solid #ccc;
    overflow-y: auto;
    padding: 10px;
    margin-bottom: 10px;
}

.message {
    margin: 10px 0;
    padding: 10px;
    border-radius: 5px;
}

.message.sent {
    background-color: #e3f2fd;
    margin-left: 20%;
}

.message.received {
    background-color: #f5f5f5;
    margin-right: 20%;
}

.chat-input {
    display: flex;
    gap: 10px;
}

input {
    flex: 1;
    padding: 10px;
    border: 1px solid #ccc;
    border-radius: 5px;
}

button {
    padding: 10px 20px;
    background-color: #2196f3;
    color: white;
    border: none;
    border-radius: 5px;
    cursor: pointer;
}
Enter fullscreen mode Exit fullscreen mode
  1. Client-side JavaScript
// public/app.js
const socket = io();

function sendMessage() {
    const messageInput = document.getElementById('message');
    const message = messageInput.value;

    if (message.trim()) {
        socket.emit('chat message', message);
        messageInput.value = '';
    }
}

socket.on('chat message', (msg) => {
    const messagesDiv = document.getElementById('messages');
    const messageElement = document.createElement('div');
    messageElement.classList.add('message');
    messageElement.classList.add('received');
    messageElement.textContent = msg;
    messagesDiv.appendChild(messageElement);
    messagesDiv.scrollTop = messagesDiv.scrollHeight;
});
Enter fullscreen mode Exit fullscreen mode

Enhanced Features

  1. User Authentication
// server.js
const users = new Map();

io.on('connection', (socket) => {
    socket.on('user join', (username) => {
        users.set(socket.id, username);
        io.emit('user joined', username);
    });

    socket.on('disconnect', () => {
        const username = users.get(socket.id);
        if (username) {
            io.emit('user left', username);
            users.delete(socket.id);
        }
    });
});
Enter fullscreen mode Exit fullscreen mode

  1. Message History
// server.js
const messageHistory = [];

io.on('connection', (socket) => {
    // Send message history to new users
    socket.emit('message history', messageHistory);

    socket.on('chat message', (msg) => {
        const username = users.get(socket.id);
        const message = {
            user: username,
            text: msg,
            time: new Date().toISOString()
        };
        messageHistory.push(message);
        io.emit('chat message', message);
    });
});
Enter fullscreen mode Exit fullscreen mode
  1. Typing Indicators
// server.js
io.on('connection', (socket) => {
    socket.on('typing', () => {
        const username = users.get(socket.id);
        socket.broadcast.emit('user typing', username);
    });

    socket.on('stop typing', () => {
        const username = users.get(socket.id);
        socket.broadcast.emit('user stop typing', username);
    });
});
Enter fullscreen mode Exit fullscreen mode

Error Handling

  1. Connection Errors
// server.js
io.on('connection', (socket) => {
    socket.on('error', (error) => {
        console.error('Socket error:', error);
        socket.emit('error', 'An error occurred');
    });
});
Enter fullscreen mode Exit fullscreen mode
  1. Reconnection Logic
// public/app.js
socket.on('connect_error', (error) => {
    console.error('Connection error:', error);
    // Implement reconnection logic
    setTimeout(() => {
        socket.connect();
    }, 5000);
});
Enter fullscreen mode Exit fullscreen mode

Best Practices

  1. Security Input Validation
function validateMessage(message) {
    return message.trim().length > 0 && message.length <= 1000;
}
Enter fullscreen mode Exit fullscreen mode

Rate Limiting

const rateLimit = require('express-rate-limit');

const limiter = rateLimit({
    windowMs: 15 * 60 * 1000,
    max: 100
});

app.use(limiter);
Enter fullscreen mode Exit fullscreen mode
  1. Performance Message Batching
let messageQueue = [];
const BATCH_INTERVAL = 100;

setInterval(() => {
    if (messageQueue.length > 0) {
        io.emit('message batch', messageQueue);
        messageQueue = [];
    }
}, BATCH_INTERVAL);
Enter fullscreen mode Exit fullscreen mode

Connection Pooling

const pool = require('socket.io-redis');
io.adapter(pool({ host: 'localhost', port: 6379 }));
Enter fullscreen mode Exit fullscreen mode

Testing

  1. Unit Tests
// test/chat.test.js
const { expect } = require('chai');
const io = require('socket.io-client');

describe('Chat Application', () => {
    let clientSocket;

    beforeEach((done) => {
        clientSocket = io('http://localhost:3000');
        clientSocket.on('connect', done);
    });

    afterEach(() => {
        clientSocket.close();
    });

    it('should receive message', (done) => {
        clientSocket.emit('chat message', 'Hello');
        clientSocket.on('chat message', (msg) => {
            expect(msg).to.equal('Hello');
            done();
        });
    });
});
Enter fullscreen mode Exit fullscreen mode
  1. Load Testing
// test/load.test.js
const autocannon = require('autocannon');

autocannon({
    url: 'http://localhost:3000',
    connections: 100,
    duration: 10
}, console.log);
Enter fullscreen mode Exit fullscreen mode

Deployment

  1. Environment Configuration
// config.js
module.exports = {
    development: {
        port: 3000,
        redis: {
            host: 'localhost',
            port: 6379
        }
    },
    production: {
        port: process.env.PORT,
        redis: {
            host: process.env.REDIS_HOST,
            port: process.env.REDIS_PORT
        }
    }
};
Enter fullscreen mode Exit fullscreen mode
  1. Docker Setup
# Dockerfile
FROM node:14

WORKDIR /app

COPY package*.json ./
RUN npm install

COPY . .

EXPOSE 3000

CMD ["node", "server.js"]
Enter fullscreen mode Exit fullscreen mode

Conclusion
This guide has covered:

WebSocket basics
Real-time chat implementation
User authentication
Message history
Typing indicators
Error handling
Security measures
Performance optimization
Testing strategies
Deployment considerations


Next Steps
Add user authentication
Implement message persistence
Add file sharing
Implement private messaging
Add emoji support
Resources
Socket.IO Documentation
WebSocket API
Node.js Documentation
Express.js Documentation
Citations
WebSocket Protocol
Socket.IO Best Practices
Node.js Security Best Practices
Real-time Web Applications
🚀 Ready to kickstart your tech career?
👉 [Apply to 10000Coders]
🎓 [Learn Web Development for Free]
🌟 [See how we helped 2500+ students get jobs]

Top comments (0)