DEV Community

Cover image for Building a simple real-time chat app with Node.js and Socket.io
Ndulue Emeka
Ndulue Emeka

Posted on

Building a simple real-time chat app with Node.js and Socket.io

Communication is more important than ever in today’s fast-paced world. Real-time chat apps have become indispensable as the demand for quick and easy ways to engage with others increases. But have you ever pondered how these apps are developed? So, no more wondering! I’ll walk you through the process of creating a simple real-time chat app in this article, providing you the ability to develop a platform for effortless collaboration.

Introduction

A real-time chat application is a software that allows prompt communication between users over a network or the internet. Such applications leverage WebSockets or long-polling techniques to establish and maintain a persistent, bidirectional communication channel between a client and a server, allowing messages to be sent and also received in real-time. The client sends and also receives data to and from the server whenever it is available, allowing messages to appear instantly on the user’s screen. This is in contrast to web applications, where clients make requests to the server and wait for a response before displaying data to the user.

Developing a real-time chat app necessitates proficiency in various areas of web development, including front-end and back-end development, as well as networking. Knowledge of specific technologies and frameworks such as Node.js, Socket.io, and other WebSockets libraries is highly important to build such applications. Common examples of real-time chat applications are messaging platforms like Slack, WhatsApp, and Facebook Messenger.

Why choose Node.js and Socket.io?

Node.js and Socket.io offers a number of benefits which includes:

Real-time functionality: Socket.io is a javascript library that supports real-time, bidirectional communication among clients and servers, thereby, making it an ideal choice for designing real-time chat applications. Socket.io uses WebSockets under the hood, which enables low-latency, real-time data transfer.

Scalability: Node.js is designed to be highly scalable, meaning it can handle an overwhelming number of simultaneous connections without lagging or becoming unresponsive, making it the right choice for building real-time chat applications that support thousands or even millions of users.

Cross-platform compatibility: Node.js as a programming language is compatible with a number of operating systems, including Windows, Linux, and macOS. This means that engineers can code once and deploy it on multiple platforms, making it easier and faster to develop and maintain real-time chat applications across different devices and environments.

Setting up the project

Installation of Node.js and npm

  • Visit the official Node.js website.

  • Download the Node.js installer for your operating system (Windows, macOS, or Linux) on the homepage.

  • Once the installer is downloaded, run it and follow the on-screen instructions to install Node.js and npm on your system.

  • To verify that Node.js and npm have been successfully installed, open a command prompt (Windows) or terminal (macOS or Linux) and run the command to check the version of Node.js that has been installed.

node -v
Enter fullscreen mode Exit fullscreen mode

and then type

npm -v
Enter fullscreen mode Exit fullscreen mode

to check the version of npm installed.

Setting up a Node.js project

  • Open your command terminal and navigate to the directory where you want to create your new Node.js project.

  • Type the command to initialize a new Node.js project:

npm init
Enter fullscreen mode Exit fullscreen mode
  • You will be prompted to enter various details about your project, such as the project name, version, description, etc.

  • Ensure you follow the prompts and enter the required information. If you’re not sure about any of the prompts given, you simply press Enter to accept the default values.

  • Once you’ve entered all the required information, npm will generate a package.json file in your project directory. This file will contain information about your project and its dependencies and is used by npm to manage your project’s dependencies.

Installation of the necessary dependencies

  • Run the following command to install the dependencies listed in the package.json file:
npm install
Enter fullscreen mode Exit fullscreen mode

This will install the dependencies listed in the package.json file, in the node_modules folder in your project directory.

If you want to install a specific dependency, run the following command:

npm install <package-name> - save
Enter fullscreen mode Exit fullscreen mode

Replace with the name of the package you want to install. The — save flag will add the package to your project’s dependencies in the package.json file.

Developing the chat server

Setting up the server using the Express.js framework

  • Run the command on your command prompt
npm i express
Enter fullscreen mode Exit fullscreen mode

This will install Express.js as a dependency for your project using the npm package manager.

  • Now create a new JavaScript file for your server, and name it “server.js”.

  • Import the Express.js module by requiring it at the top of your JavaScript file using the following code:

const express = require('express');
Enter fullscreen mode Exit fullscreen mode
  • Instantiate the Express application by calling the express() function and assigning it to a variable.
const app = express();
Enter fullscreen mode Exit fullscreen mode
  • Set up routes on your server by specifying endpoints for the HTTP methods you want to handle, this includes GET, POST, PUT, and DELETE methods. Here is an example a GET endpoint at the root of your server:
app.get('/', (req, res) => {
  res.send('Welcome!');
});
Enter fullscreen mode Exit fullscreen mode
  • Start the server by calling the “listen” method on the Express application instance, passing in the port number “3000” your server will listen on as an argument:
app.listen(3000, () => {
  console.log('Server is listening on port 3000');
});
Enter fullscreen mode Exit fullscreen mode
  • Run your server by running the “node” command along with the name of your server file in the terminal or command prompt. For example, to run a server saved in a file called “server.js”, you would type “node server.js”.

Once your server is up and running, you should be able to access it in your web browser by visiting “http://localhost:3000/".

Setting up Socket.io on the server

  • Run the command to install Socket.io in your project directory as a dependency for your project using the npm package manager.
npm install socket.io
Enter fullscreen mode Exit fullscreen mode
  • Import both the Express.js and Socket.io modules by requiring them at the top of your JavaScript file using the following code:
const express = require('express');
const app = express();
const http = require('http').createServer(app);
const io = require('socket.io')(http);
Enter fullscreen mode Exit fullscreen mode

Here, Express.js instance is wrapped with the HTTP server instance to create an HTTP server that can handle both WebSocket connections and regular HTTP requests.

  • Set up the connection event handler for Socket.io, which listens for incoming socket connections and executes a callback function whenever a connection is established.
io.on('connection', (socket) => {
  console.log('Connected');
});
Enter fullscreen mode Exit fullscreen mode
  • Proceed to set up event listeners for the different events that you want to handle on the server side, such as “typing” or “message”. To emit events to all connected clients use the “io.emit()” method, or to a specific client use the “socket.emit()” method. Here is an example of emitting a “message” event to all connected clients:
io.on('connection', (socket) => {
  console.log('Connected');

  socket.on('message', (data) => {
    console.log('Your Message: ', data);
    io.emit('message', data);
  });

});
Enter fullscreen mode Exit fullscreen mode
  • Start the server by calling the “listen” method on the http application instance, passing in the port number “3000” your server will listen on as an argument:
http.listen(3000, () => {
  console.log('Server listening on port 3000');
});
Enter fullscreen mode Exit fullscreen mode

Once you have configured Socket.io on your server, you can use the socket object in your event listeners to communicate with connected clients in real-time, sending and receiving messages or data as needed.

Creating event listeners for incoming socket connections

Establish a connection event handler using the “io.on” method to listen for incoming socket connections. Inside the callback function for the “io.on” method, your event listeners can be created using the “socket.on” method.

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

  console.log('A user connected');

  socket.on('message', (data) => {
    console.log('Received message:', data);
  });

});
Enter fullscreen mode Exit fullscreen mode

In this example, we use the “socket.on” method to create a “message” event listener for the newly connected client. Whenever the client emits a “message” event, the callback function is executed, and we log the received message to the console.

You can create multiple event listeners inside the connection event handler to handle various events from the clients. When the client emits an event, the corresponding event listener function is executed on the server, allowing you to handle the event in real-time and perform any necessary actions.

Setting up basic chat functionality with socket events

To set up basic chat functionality with Socket.io events, you can create event listeners to handle sending messages, joining/leaving rooms, and other relevant actions.

Here is an example of how to create event listeners for these actions:

io.on('connection', (socket) => {
  console.log('A user connected');
  // Join a room
  socket.on('joinRoom', (room) => {

    console.log(`${socket.id} just joined room ${room}`);

    socket.join(room);

    io.to(room).emit('roomJoined', `${socket.id} just joined the room`);
  });

  // Leave a room
  socket.on('leaveRoom', (room) => {
    console.log(`${socket.id} has left room ${room}`);

    socket.leave(room);

    io.to(room).emit('roomLeft', `${socket.id} has left the room`);
  });


  // Post a message to a specific room
  socket.on('messageToRoom', (data) => {

    console.log(`${socket.id} posted a message to room ${data.room}: ${data.message}`);

    io.to(data.room).emit('message', {
      id: socket.id,
      message: data.message
    });

  });


  // Send a message to all connected clients
  socket.on('messageToAll', (data) => {
    console.log(`${socket.id} sent a message to all clients: ${data.message}`);

    io.emit('message', {
      id: socket.id,
      message: data.message
    });  

  });
  // Disconnect event
  socket.on('disconnect', () => {

    console.log(`${socket.id} disconnected`);

  });

});
Enter fullscreen mode Exit fullscreen mode

In this example, we create a connection event listener that logs a message to the console when a new socket connection is made. We also create event listeners for various actions, inside the connection event listener.

First, we create event listeners to handle both joining and leaving rooms. When a client emits a “joinRoom” event, we use the “socket.join” method to add the client to the specified room and emit a “roomJoined” event to all clients in the room. Similarly, when a client also emits a “leaveRoom” event, we use the “socket.leave” method to remove the client from the specified room and emit a “roomLeft” event to all clients in the room.

Next, we initialize event listeners to handle sending messages effectively. We use the “io.to” method to emit the “message” event to all clients in the room when a client emits the “messageToRoom” event. We also use the “io.emit” method to emit a “message” event to all connected clients, whenever a client emits a “messageToAll” event.

Lastly, an event listener is created to handle the “disconnect” event, which is emitted when a client gets disconnected from the server. A message is logged indicating that the client is disconnected, when this event is emitted.

Creating the chat client

Creating the chat client entails developing the user interface for the chat application which allows users to interact with the server. Here are the steps involved in this procedure:

  • The first step is to write the client-side JavaScript code that will connect to the server via Socket.io. This entails integrating the Socket.io client library in your HTML code and establishing a new Socket.io client instance:
<script src="/socket.io/socket.io.js"></script>
<script>
  const socket = io();
</script>
Enter fullscreen mode Exit fullscreen mode
  • Join a chat room: To join a chat room, you send a “joinRoom” event to the server with the desired room ID, on receiving this event, the server adds the user to the specified room and emit a “userJoined” event to other clients in that same room.
socket.emit('joinRoom', roomId);
Enter fullscreen mode Exit fullscreen mode
  • Send messages: To send a message, listen for the “submit” event on the message input form and emit a “sendMessage” event to the server, on receiving the “sendMessage” event, the server will disseminate the message to other users in the same room using the “newMessage” event.
const messageForm = document.querySelector('#message-form');
messageForm.addEventListener('submit', (event) => {

  event.preventDefault();
  const messageInput = document.querySelector('#message-input');

  const message = {
    text: messageInput.value
  };

  socket.emit('sendMessage', message);
  messageInput.value = '';

});
Enter fullscreen mode Exit fullscreen mode
  • Receive messages: In order to receive messages from other users, listen for the “newMessage” event on the client-side and update the UI accordingly:
socket.on('newMessage', (message) => {

  const messagesList = document.querySelector('#messages-list');

  const messageItem = document.createElement('li');

  messageItem.textContent = `${message.userId}: ${message.text}`;

  messagesList.appendChild(messageItem);

});
Enter fullscreen mode Exit fullscreen mode

This code above makes an entire list item element for each message and adds it to the chat window.

  • Handle errors and edge cases: Lastly, handle errors and edge cases in the client-side code, such as network errors, disconnections, and invalid input. Listen for the “disconnect” event on the client-side to detect when the server connection is lost and update the UI accordingly:
socket.on('disconnect', () => {
  // Update UI to indicate that the user is disconnected
});
Enter fullscreen mode Exit fullscreen mode

Additionally, validate user input before sending it to the server and display error messages if the input is invalid.

Connecting to the chat server with Socket.io

To establish a client-side Socket.io connection in a Node.js application, follow these steps:

  • Install the Socket.io client library by running this command in your project directory, this will download and install Socket.io client library in your project.
npm install socket.io-client
Enter fullscreen mode Exit fullscreen mode
  • Load the Socket.io client library in your client-side JavaScript code after installing it by including the following line in your HTML file, which will load the it from the server:
<script src="/socket.io/socket.io.js"></script>
Enter fullscreen mode Exit fullscreen mode
  • After successfully loading the Socket.io client library, connect to the server by establishing a Socket.io client instance and supplying the server URL, as shown below:
const socket = io('http://localhost:3000');
Enter fullscreen mode Exit fullscreen mode

This will create a new Socket.io client instance and attempt to connect to the server at the supplied URL.

  • Emit events to the server using the socket object, Once a connection is established with the server. For example, you can emit a “joinRoom” event to the server to join a chat room:
socket.emit('joinRoom', roomId);
Enter fullscreen mode Exit fullscreen mode

Here, roomId is a variable that contains the ID of the chat room you want to join.

  • Listen for events from the server using the socket object. To get new chat messages from the server, we listen for a “newMessage” event here:
socket.on('newMessage', (message) => {
  console.log(`Received message: ${message.text}`);
});
Enter fullscreen mode Exit fullscreen mode

Here, message is a variable that contains the new chat message received from the server.

Creating event listeners for incoming socket events

To create event listeners for incoming socket events in a Node.js chat application, you will need to use the socket.on() method both on the server-side and on the client-side.

Create a server-side listener for incoming events. To receive new chat messages from the client, for example, listen for a “chatMessage” event, as seen in the following code:

socket.on('chatMessage', (message) => {

  console.log(`Received message: ${message.text}`);

  // Send the message to other users
  socket.broadcast.to(message.room).emit('newMessage', message);

});
Enter fullscreen mode Exit fullscreen mode

Here, socket refers to the incoming socket connection, and message is a variable containing the new chat message received from the client. The console.log statement simply logs the incoming message to the server console. The socket.broadcast.to(message.room).emit() method is used to broadcast the message to all other clients in the same chat room.

  • Listen for the identical “newMessage” event that the server emitted in response to the incoming “chatMessage” event on the client-side, as illustrated in this code:
socket.on('newMessage', (message) => {

  console.log(`Received message: ${message.text}`);

  // Update the UI with the new message
  displayMessage(message);

});
Enter fullscreen mode Exit fullscreen mode

Here, socket refers to the client-side socket connection, and message is a variable that contains the new chat message received from the server, the console.log statement logs the incoming message to the client console, while the displayMessage() function is a custom function that displays the new message in the UI.

To send events from the client to the server, use the socket.emit() method. To send a new chat message to the server from the client, for example, emit a “chatMessage” event containing the message data, as illustrated in this code:

socket.emit('chatMessage', { text: messageText, room: roomId });
Here, socket refers to the client-side socket connection, and messageText is the text of the new chat message. roomId is the ID of the chat room where the message should be sent.

Developing a User Interface for chat functionality

Here are some steps to get started:

Make a basic HTML layout for your chat application. This should feature a header, a chat room display area, a message input box, and a list of online users. To structure your layout, you employ semantic HTML tags such as header, main, section, ul, and li.

Make your HTML layout more visually appealing by including CSS styles. Use CSS properties like background-color, border, padding, font-size, and text-align to customize the appearance of your chat UI.

Use JavaScript to connect to the chat server using Socket.io. Use the io() function to create a new socket connection in your client-side JavaScript code.

Create event listeners to handle incoming socket events. For example, to receive new chat messages from the server, listen for the “newMessage” event, and update the UI accordingly. You can also listen for the “userList” event to receive a list of online users, and update the UI with it.

Use JavaScript to refresh the UI with new chat messages and user online status. Also use DOM manipulation methods like document.createElement(), element.appendChild(), and element.innerHTML to dynamically create and update HTML elements in response to incoming socket events.

Finally, style the chat messages and online user list using CSS. CSS classes and selectors can also be used for adding styles to particular components within the HTML layout.

Here’s an example of how you can display incoming chat messages in your chat UI:

function displayMessage(message) {
  const messageContainer = document.querySelector('#message-container');

  const messageElement = document.createElement('div');

  messageElement.classList.add('message');

  messageElement.innerHTML = `<span class="username">${message.username}: </span>${message.text}`;

  messageContainer.appendChild(messageElement);
}
Enter fullscreen mode Exit fullscreen mode

Here, message is a variable that contains the new chat message received from the server. The displayMessage() function adds the message text to a new div element with the class “message” and appends it to the messageContainer element in the HTML layout.

Similarly, here’s an example of how you can update the online user list in the chat’s UI:

function updateUserList(users) {
  const userList = document.querySelector('#user-list');
  userList.innerHTML = '';
  users.forEach(user => {
    const userElement = document.createElement('li');
    userElement.textContent = user.username;
    userList.appendChild(userElement);
  });
}
Enter fullscreen mode Exit fullscreen mode

Here, users is a variable that holds the most recent list of online users received from the server. The updateUserList() function clears the HTML layout’s existing user list, loops through the users array, generates a new li element for each user, and appends it to the userList element.

Adding new chat functionalities

We make use Socket.io’s built-in features and add some custom logic to both client and server code to provide additional chat features such as private messaging, message history, and notifications. Here are a couple of such examples:

Private messaging: To enable private messaging, we create a new event on the server called private message which takes in a message and a recipient. While on the client side, create a form for sending private messages that sends a private message event to the server along with the message and the recipient.

// Server-side code
socket.on('private message', function(msg, recipient) {
  // Send a private message to the recipient
});

// Client-side code
const recipient = 'user01';
const message = 'Good Morning';
socket.emit('private message', message, recipient);
Enter fullscreen mode Exit fullscreen mode

Displaying message history: Create a new event on the server to display message history called chat history which sends the chat history to the client when it connects. On the client side, create a function that listens for the chat history event and updates the chat UI with the previous messages.

// Server-side code
socket.on('connection', function() {
  // Send chat history to the connected client
  socket.emit('chat history', chatHistory);
});

// Client-side code
socket.on('chat history', function(history) {
  // Update chat UI with message history
});
Enter fullscreen mode Exit fullscreen mode

Enable Notifications: Create a new event to send notifications on the server called notification that delivers a notification message to all connected clients. While on the client side, create a function that listens for the notification event and displays a notification message to the user.

// Server-side code
function sendNotification(message) {
  // Push notification to all clients
  io.emit('notification', message);
}

// Client-side code
socket.on('notification', function(message) {
  // Display notification message to the user
});
Enter fullscreen mode Exit fullscreen mode

By implementing these additional chat features, we can make our real-time chat application more useful and user-friendly.

Using Giphy to enhance the conversation experience

External APIs such as Giphy adds more fun and interactivity to the chat experience. Here’s an example of how we can integrate Giphy API into our chat application:

First, we obtain an API key from Giphy by signing up for their developer program. Then, we use a library like axios to make HTTP requests to the Giphy API and fetch GIFs based on user input.

const apiKey = 'your_api_key_here';

const apiUrl = `https://api.giphy.com/v1/gifs/search?api_key=${apiKey}`;

function searchGifs(query) {
  return fetch(`${apiUrl}&q=${query}`)
  .then(response => response.json())
  .then(data => {
    const gifUrls = data.data.map(gif => gif.images.original.url);
    return gifUrls;
  });
}
Enter fullscreen mode Exit fullscreen mode

On the client side, we provide an input field where users search for GIFs and send a message with the selected GIF to the chat.

<! - HTML code for the input field →
<input type="text" id="gif-search" placeholder="Search for a GIF">
<button id="gif-search-btn">Search</button>


// Client-side code for searching and sending GIFs
const searchInput = document.getElementById('gif-search');
const searchBtn = document.getElementById('gif-search-btn');
searchBtn.addEventListener('click', function() {
  const query = searchInput.value;
  searchGifs(query)
  .then(gifUrls => {
    // Select a GIF from the results
    const gifUrl = gifUrls[Math.floor(Math.random() * gifUrls.length)];
    // Send a message with the GIF to the chat
    const message = `<img src="${gifUrl}" alt="GIF"/>`;
    socket.emit('chat message', message);
    })
    .catch(error => {
    console.error(error);
  });
});
Enter fullscreen mode Exit fullscreen mode

By integrating Giphy API or other external APIs, we add more engaging features to our chat application, making it significantly more appealing and interactive for users.

Conclusion

In conclusion, a real-time chat application is an online application that allows users to effectively communicate with each other in real-time using technologies such as Node.js, Express.js, and Socket.io through text messages. Building a real-time chat application may be a fun and dynamic way for users to converse while also learning and practicing web development skills.

To create a real-time chat application, we must first set up the server using Express.js, then configure Socket.io on the server, create event listeners for incoming socket connections, implement basic chat functionality with socket events, handle edge cases, create the chat client, and implement additional chat features such as private messaging, message history, and external APIs such as Giphy.

Top comments (0)