Build Realtime Chat App with Socket.io, Node & Express

Find AI Tools
No difficulty
No complicated process
Find ai tools

Build Realtime Chat App with Socket.io, Node & Express

Table of Contents

  1. Introduction
  2. Building a Real-Time Chat Application
    1. Requirements
    2. Setting Up the Development Environment
    3. Creating the HTML and CSS for the Application
    4. Initializing the Node.js Server
    5. Implementing WebSockets using Socket.IO
    6. Handling User Join and Leave Events
    7. Broadcasting Messages to the Chat Room
    8. Formatting Messages and Adding Timestamps
    9. Tracking Users and Rooms
    10. Displaying Users and Rooms in the Sidebar
  3. Conclusion

Building a Real-Time Chat Application

Introduction

In this tutorial, we will be building a real-time chat application from scratch using Node.js, Express, and WebSockets with Socket.IO. The application will allow users to join different chat rooms and communicate with other users in real-time.

Requirements

To follow along with this tutorial, You will need to have Node.js and npm (Node Package Manager) installed on your computer. You should also have a basic understanding of HTML, CSS, and JavaScript.

Setting Up the Development Environment

Before we start building the chat application, we need to set up our development environment. We will be using Express as our web framework and Socket.IO for handling WebSockets. To begin, we need to initialize a package.json file by running the command npm init. This will prompt us to enter information about our project, such as the package name, version, description, entry point, author, and license.

Next, we will install the necessary dependencies for our project. Run the following command in your terminal: npm install express socket.io moment. This will install Express, Socket.IO, and Moment.js, which we will use for formatting dates and times.

Creating the HTML and CSS for the Application

We will start by creating the HTML and CSS for our chat application. In the public folder, Create two HTML files: index.html and chat.html. In index.html, create a landing page where users can enter their username and choose a room to join. You can customize the design and layout of the landing page as desired.

In chat.html, create the chat interface where users can send and receive messages in real-time. The interface should display the room name, list of users in the room, and a message area where users can input their messages. Again, you have the freedom to customize the design and layout of the chat interface.

Create a CSS file to style the HTML pages. You can include the necessary CSS rules to create an appealing and user-friendly design for the chat application.

Initializing the Node.js Server

Now we can create the server entry point. In the root directory of your project, create a file called server.js. This will serve as the main file for our Node.js server. In server.js, we will initialize an Express server and set it to listen on a specified port.

First, we need to import the necessary modules. Add the following code at the top of your server.js file:

const express = require('express');
const app = express();
const http = require('http').Server(app);
const io = require('socket.io')(http);

These lines of code import the required modules, create an instance of Express, create an HTTP server using the Express app, and initialize Socket.IO with the http server.

Next, we need to define the routes and static folder for our server. Add the following code before the server starts listening:

app.use(express.static('public'));

app.get('/', (req, res) => {
  res.sendFile(__dirname + '/public/index.html');
});

app.get('/chat', (req, res) => {
  res.sendFile(__dirname + '/public/chat.html');
});

These lines of code set the public folder as the static folder for serving static files such as HTML, CSS, and JavaScript files. Then, we define two routes: one for the landing page (/) and one for the chat page (/chat). When these routes are accessed, we send the corresponding HTML file to the client.

Finally, we can start the server by adding the following code:

const PORT = process.env.PORT || 3000;

http.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

This code sets the server to listen on the specified port. If the PORT environment variable is set, it will use the value of that variable. Otherwise, it will default to port 3000.

Implementing WebSockets using Socket.IO

Now that our server is set up, we can begin implementing WebSockets using Socket.IO. WebSockets allow for bi-directional communication between the client and the server, enabling real-time updates without the need for page refreshes.

In server.js, within the connection event, add the following code:

io.on('connection', (socket) => {
  console.log('New WebSocket connection');

  socket.on('chat message', (msg) => {
    io.emit('chat message', msg);
  });
});

This code sets up a connection event listener that logs a message to the server's console whenever a new WebSocket connection is established. It also sets up a listener for the chat message event, which is emitted by the client when a new message is sent. When a message is received, it is broadcasted to all connected clients using the io.emit method.

On the client-side, open chat.html and add the following JavaScript code to establish a WebSocket connection and handle the sending and receiving of messages:

<script src="/socket.io/socket.io.js"></script>

const socket = io();

socket.on('chat message', (msg) => {
  // Handle received message
});

// Handle form submission
const form = document.getElementById('chat-form');
const input = document.getElementById('message-input');

form.addEventListener('submit', (e) => {
  e.preventDefault();

  const message = input.value;

  // Emit chat message event to server
  socket.emit('chat message', message);

  input.value = '';
  input.focus();
});

This code includes the Socket.IO library and establishes a WebSocket connection with the server using const socket = io(). It sets up a listener for the chat message event, which is emitted by the server when a new message is received. The received message can be handled as desired.

The code also handles the form submission event by preventing the default form behavior and emitting the chat message event to the server with the inputted message. Finally, it clears the input field and sets focus back to it.

With this implementation, messages can be sent from the client to the server and then broadcasted to all connected clients in real-time.

Handling User Join and Leave Events

To create a better user experience, we want to notify other users in the chat room when a new user joins or leaves. We can do this by emitting separate WebSocket events for user join and leave events.

In server.js, within the io.on('connection') event listener, add the following code:

socket.on('join room', ({ username, room }) => {
  const user = userJoin(socket.id, username, room);

  socket.join(user.room);

  socket.emit('message', formatMessage('Chat Cord Bot', `Welcome to the chat, ${user.username}!`));
  socket.broadcast.to(user.room).emit('message', formatMessage('Chat Cord Bot', `${user.username} has joined the chat`));

  io.to(user.room).emit('room users', {
    room: user.room,
    users: getRoomUsers(user.room)
  });
});

socket.on('disconnect', () => {
  const user = userLeave(socket.id);

  if (user) {
    io.to(user.room).emit('message', formatMessage('Chat Cord Bot', `${user.username} has left the chat`));

    io.to(user.room).emit('room users', {
      room: user.room,
      users: getRoomUsers(user.room)
    });
  }
});

This code handles two events: join room and disconnect.

In the join room event listener, we first extract the username and room from the data object passed in. Then, we use the userJoin function to add the user to our array of users. This function assigns a unique ID to the user, and we store their username and room information.

Next, we call socket.join(user.room) to add the user to the specified room.

We also emit a message event to welcome the user to the chat, both individually and to the room. This is done using socket.emit and socket.Broadcast.to(user.room).emit. The messages are formatted using the formatMessage function, which creates an object with the username and message text.

Lastly, we emit a room users event to update the list of users in the room for all connected clients. This event sends an object containing the room name and an array of users in that room. The array is obtained using the getRoomUsers function.

In the disconnect event listener, we use the userLeave function to remove the user from the array when they disconnect. If the function returns a user (indicating that the user was in fact in the array), we emit a message event to notify other users in the room that the user has left. We also emit a room users event to update the list of users in the room.

On the client-side, add the following code to handle the message and room users events:

socket.on('message', (message) => {
  // Handle received message
});

socket.on('room users', ({ room, users }) => {
  // Handle received room users data
});

This code adds event listeners for the message and room users events. The received message and room users data can be handled as desired.

With these event listeners in place, users will be notified when someone joins or leaves the chat, and the list of users in the room will be updated in real-time.

Broadcasting Messages to the Chat Room

Currently, all messages sent by a user are broadcasted to the entire chat room. To improve the user experience, we can emit the messages only to users in the same room. This way, users in different rooms will not see messages sent by users in other rooms.

In server.js, within the io.on('connection') event listener, modify the socket.on('chat message') event listener as follows:

socket.on('chat message', (msg) => {
  const user = getCurrentUser(socket.id);

  io.to(user.room).emit('message', formatMessage(user.username, msg));
});

This code first retrieves the Current user using the getCurrentUser function. Then, it emits the message event to all connected clients in the same room using io.to(user.room).emit. The message is formatted using the formatMessage function, which creates an object with the username and message text.

On the client-side, within the socket.on('message') event listener, modify the code that handles the received message as desired. For example, you can update the chat interface to display the username and message in a visually appealing manner.

With this modification, messages sent by users will only be seen by other users in the same room, providing a more focused and personalized chat experience.

Formatting Messages and Adding Timestamps

To improve the readability of messages, we can format them by adding timestamps and styling the username. This will make it easier for users to identify the sender of each message and when it was sent.

In server.js, within the io.on('connection') event listener, create a new function called formatMessage:

const formatMessage = (username, text) => {
  return {
    username,
    text,
    time: moment().format('h:mm a')
  };
};

This function creates an object with the username, text, and time properties. The time is obtained using the Moment.js library and is formatted to display hours, minutes, and the AM or PM indicator.

Now, modify the socket.on('chat message') event listener to use the formatMessage function:

socket.on('chat message', (msg) => {
  const user = getCurrentUser(socket.id);

  io.to(user.room).emit('message', formatMessage(user.username, msg));
});

On the client-side, within the socket.on('message') event listener, modify the code that handles the received message as desired. Utilize the message.username, message.text, and message.time properties to display the username, chat message, and timestamp respectively.

By adding timestamps and styling the username, messages in the chat interface will be more informative and visually appealing.

Tracking Users and Rooms

To provide a better user experience, we can track the users and rooms in the chat application. This will allow us to display a sidebar with a list of users in the current room and a dropdown menu to switch between different rooms.

In utils/users.js, create the following functions:

const users = [];

// Join user to chat
const userJoin = (id, username, room) => {
  const user = { id, username, room };
  users.push(user);
  return user;
};

// Remove user from chat
const userLeave = (id) => {
  const index = users.findIndex((user) => user.id === id);

  if (index !== -1) {
    return users.splice(index, 1)[0];
  }
};

// Get current user
const getCurrentUser = (id) => {
  return users.find((user) => user.id === id);
};

// Get users in room
const getRoomUsers = (room) => {
  return users.filter((user) => user.room === room);
};

module.exports = {
  userJoin,
  userLeave,
  getCurrentUser,
  getRoomUsers
};

These functions work together to manage the users in the chat application. Users are stored in an array called users. The userJoin function adds a user to the array, userLeave removes a user from the array, getCurrentUser retrieves the current user Based on their unique ID, and getRoomUsers retrieves an array of users in a specific room.

In server.js, import the users module and modify the io.on('connection') event listener as follows:

const {
  userJoin,
  userLeave,
  getCurrentUser,
  getRoomUsers,
} = require('./utils/users');

// ...

io.on('connection', (socket) => {
  // ...

  socket.on('join room', ({ username, room }) => {
    const user = userJoin(socket.id, username, room);

    socket.join(user.room);

    socket.emit('message', formatMessage('Chat Cord Bot', `Welcome to the chat, ${user.username}!`));
    socket.broadcast.to(user.room).emit('message', formatMessage('Chat Cord Bot', `${user.username} has joined the chat`));

    io.to(user.room).emit('room users', {
      room: user.room,
      users: getRoomUsers(user.room),
    });
  });

  socket.on('disconnect', () => {
    const user = userLeave(socket.id);

    if (user) {
      io.to(user.room).emit('message', formatMessage('Chat Cord Bot', `${user.username} has left the chat`));

      io.to(user.room).emit('room users', {
        room: user.room,
        users: getRoomUsers(user.room),
      });
    }
  });
});

By utilizing these functions, we can now track the users and rooms within the chat application. This allows us to easily manage and display the list of users in the sidebar.

Displaying Users and Rooms in the Sidebar

To provide an overview of the current room and the list of users in the sidebar, we can update the HTML file with dynamic content.

In chat.html, create a div with the id room-info to display the current room name, and a ul with the id user-list to display the list of users in the room. These elements will be updated dynamically using JavaScript.

In the client-side JavaScript code, we can now handle the room users event to update the room information and user list in the sidebar:

socket.on('room users', ({ room, users }) => {
  outputRoomName(room);
  outputUsers(users);
});

const outputRoomName = (room) => {
  const roomName = document.getElementById('room-info');
  roomName.innerText = room;
};

const outputUsers = (users) => {
  const userList = document.getElementById('user-list');
  userList.innerHTML = '';

  users.forEach((user) => {
    const li = document.createElement('li');
    li.innerText = user.username;
    userList.appendChild(li);
  });
};

In this code, we add event listeners for the room users event. When this event is received, we call two Helper functions: outputRoomName and outputUsers.

The outputRoomName function updates the content of the room-info element with the current room name. The outputUsers function updates the user-list element with the list of users in the room. For each user, it creates a new li element and appends it to the user-list element.

By implementing these changes, the sidebar will display the current room name and the list of users in that room. The information will be updated dynamically as users join or leave the room.

Conclusion

In this tutorial, we have built a real-time chat application using Node.js, Express, and WebSockets with Socket.IO. The application allows users to join different chat rooms and communicate with each other in real-time. We have covered the essentials of building a chat application, including handling user join and leave events, broadcasting messages to the chat room, formatting messages with timestamps, and tracking users and rooms.

This tutorial is just a starting point, and there are many ways to enhance and expand upon the chat application. You can explore features such as authentication, storing messages in a database, implementing private messaging, or adding additional functionality to enhance the user experience. The possibilities are endless!

Thank you for reading, and I hope you have found this tutorial helpful in creating your own real-time chat application.

Most people like

Are you spending too much time looking for ai tools?
App rating
4.9
AI Tools
100k+
Trusted Users
5000+
WHY YOU SHOULD CHOOSE TOOLIFY

TOOLIFY is the best ai tool source.

Browse More Content