Single page application in MEVN stack – Realtime chat

In this article, we are going to provide you with a free course to develop a full-fledged real-time chat app in MEVN (MongoDB, Express, Vue JS, and Node JS) Stack. Here M stands for MongoDB which we have used as a database. E stands for Express which is a Node JS framework. V stands for Vue JS which will be our front end. We will be creating a single-page application in Vue JS. And N stands for Node JS which will be our backend.

You can download Node JS from here. And Mongo DB from their official site.

FeaturesFreePremium $100
Sign upYesYes
Login/logoutYesYes
Add contactsYesYes
Private chatYesYes
Message encryptionYesYes
Chat with attachmentYesYes
Realtime chatYesYes
Email verificationNoYes
Reset passwordNoYes
User profileNoYes
Chat with emojisNoYes
Bookmark messagesNoYes
Copy messageNoYes
Archive chat with passwordNoYes
Group chatNoYes
Notifications pageNoYes
Customer supportNoYes

We will do the deployment of this app on live servers. You can find the link to all the tutorials of this series here. Following are the topics we have covered in this series.

Installation of Node JS server

In the very first tutorial, we have done the installation of the Node JS server. We also connect our Node JS server with MongoDB. You can find the tutorial here.

Setup Vue JS CLI – A single page application

Then we set up our Vue JS app using CLI. We used Vue JS CLI because we will be creating a single page application. We will be using Vue 3. Right now, we will be using the app on the local server. But once it is done, we will teach you how you can create a production build of it. And also how you can make it live on your website. This tutorial can be found here.

Routing for single page application

As this is a single page application, so we will be doing routing using Vue components. You can learn how to implement routing in a single page Vue JS app from this tutorial.

Hello world

In this part, we will learn how to integrate Bootstrap into your Vue app. So you can design the theme as per your choice. We also show you how you can create header and footer layouts in Vue JS. The header and footer will be displayed on each page. For the header, we will be creating a simple Bootstrap navbar. And for the footer, we will simply be displaying copyright text and the current year dynamically.

Registration

You will learn how to display a form using Vue JS. Sends the form values to the Node JS server. And how Node JS can save the data in the MongoDB database. You will also learn how to check if certain data exists in MongoDB or not. Moreover, you will also learn how to encrypt the passwords before saving them in MongoDB. So even if your database gets stolen, your user’s passwords will remain unknown to the hacker.

One more thing you will learn in this part is how to deal with CORS (Cross-Origin Resource Sharing) errors in the Node JS server.

Email verification

After successful registration, an email will be sent to the user’s email address. That email will have the verification code. And the user will be redirected to a page where he can enter his verification code.

He will not be able to log in until he verifies his email address.

Login

In this part, we do the authentication. You will learn how to generate JWT (JSON Web Tokens) and how to store them in local storage for future usage. You will also learn how to validate the encrypted passwords saved in MongoDB while authenticating.

Reset Password

If a user forgets his password, he can reset it. He just needs to enter his email address. An email will be sent to him with a link to reset the password. When he clicks that link, he will be redirected to a page where he can enter his new password.

Get logged-in user’s data

We will learn how to call AJAX with headers in Vue JS. How Node JS server read the headers? And how to create middleware in Node JS.

Logout

We will teach you how to log out as a user from MongoDB and from the Vue JS client app. We will remove the JWT token we create in the login part.

Contacts

To chat with people, we need to add them to our contacts. We will be creating separate components in the Vue JS app for adding and displaying all contacts. We will also be creating a new module in the Node JS server. You will learn how to insert data in a nested array in MongoDB.

We will be calling AJAX requests using Axios in Vue JS. While deleting a contact, you will learn how to ask for confirmation using sweet alert dialogs.

You will also learn how to remove an element from a nested array in MongoDB.

Chat with attachments

This is the main module of this app. We advise you to please put more focus on this part. It teaches you how to import CSS in specific Vue components only. How to send input type file from the Vue JS app to the Node JS server. How Node JS saves files on the server.

Encryption and Decryption

We are saving messages in MongoDB in encrypted form. While fetching the messages, we are again decrypting the messages back to their original state. Even if someone has access to your database, he will not be able to read your messages. Because he does not have the key. The key will only be stored on the Node JS server.

You can see the messages are fully encrypted. On the home page, where we are displaying all user’s contacts, we will also be displaying unread messages from each contact.

Attachments uploaded with messages are also protected from direct URL access. So we will show you how you can convert a file in base64 string using Node JS. And download them to your system using the Vue JS app.

Realtime chat using Socket IO

Chat will be real-time. Meaning users do not have to refresh the page to see new messages. He will automatically receive a notification. If the chat is opened, then the incoming message will be appended automatically.

We have “load more” functionality. This means that we will only show 10 messages in each AJAX call. To get the previous 10 messages, the user will click on that button and we will fetch 10 more messages. So you will learn pagination using Node JS and Mongo DB too.

Deploy Node JS, Mongo DB, and Vue JS apps

We will be deploying our Node JS server on Heroku. You can check our Heroku guide here. We will be deploying MongoDB on mongodb.com. And we will be deploying our Vue JS app on any hosting provider we choose. For example, a2hosting, Bluehost, Siteground, etc. We will create a production-ready build of our Vue JS app and upload it on our live server.

However, you might face some problems going live. That’s why we have written a detailed complete tutorial on the deployment on all these 3 platforms.

Search Contact

You can search your contacts by their email address. Searching with their email address points you to the right person. Because multiple people can have the same name. But email is unique in the entire “users” collection in MongoDB.

Group chat

Previously we discussed private chat. In this tutorial series, we also cover the group chat feature. You will be able to create groups. Add members to the groups. You will be sending an invitation to the members to join the group. The other person can accept your request to join the group or can ignore it.

Any member can leave the group anytime he wants. But this is not the case for admins. For the admin to leave the group, he must first assign someone else to the admin of the group. The other person must be a member of the group. After assigning someone else as an admin, he can easily leave the group.

People who are not members or the admin of the group, cannot send or view the messages in the group chat.

The chat in the group is also real-time. All the other members of the group will get a notification when someone sends a message. If they have opened the group, then the new message will automatically get appended at the end of the chat.

Group chat is also end-to-end encrypted.

And same as we did for private chat, we are displaying several unread messages from a group on the page where we are displaying all groups.

Emojis

In chat, you can also send emojis to the recipient. Emojis are also encrypted.

Bookmark message

You can also bookmark your favorite messages. This way, you can always see your bookmarked messages whenever you want. You can bookmark your messages as well.

Copy message

If someone sends you a lengthy message, like an essay. You can copy the whole message with a single click.

Archive chat with password

  • You can archive your private chat with a password.
  • Once archived, no one will be able to view the chat.
  • To view the chat, you must provide the correct password.
  • You can remove the password by simply entering the correct password.
  • Archived chats cannot be deleted. You must first un-archive the chat, then delete it.

Re-designed

I re-designed the website into a beautiful template. That’s how it looks now.

So you will be learning to create a full-stack chat app from scratch using Node JS, Mongo DB, and Vue JS. Again, you can find the tutorial for the complete series here. Enjoy!

Free version

https://github.com/adnanafzal565/spa-chat-app-mevn

Our Trustpilot reviews

Realtime customer support chat widget – PHP, Javascript, MySQL, Node JS

In this article, we are going to teach you how you can create a real-time customer support chat widget on your website. Users will be able to chat with the admin and get instant replies. All messages will be stored in the MySQL database.

By real-time, we mean that users or admins do not have to refresh the page to see new messages. Also, the admin will be able to view the navigation history of the user. For example, how many and which pages the user has visited, what was the last page the user has visited etc.

Tutorial:

Source code:

After buying this project, you will get the complete source code and use it in as many websites as you want.

Our Trustpilot reviews

TrustPilot-reviews
TrustPilot-reviews

Realtime Web-based Chat in Firebase

Following this tutorial, you will be able to do realtime chat in Firebase in your website. We will be using Javascript in this tutorial.

Firebase

Firebase provides the ability to use non-relational database which means data will be stored in objects and arrays. It uses JSON format to save data in database. The structure of data is similar to as stored in Mongo DB where each table is known as collection. Each row is resembled with documents inside collection.

By default, Firebase provides 15 GB of data where you can store text, images and documents too. One of the biggest advantage of using Firebase is that it has built-in support for realtime notifications. That means that when a new item is added or removed it will automatically be notified in respective events. So Firebase ease the process of setting up Socket, Node JS and Mongo DB.

Firebase console

Goto Firebase Console and create a new project. Set the name of project and click “Continue”, you can disable the analytics for now. After that, click on “web” icon to integrate Javascript API in your project. Set the name of your project again and DO NOT set Firebase hosting. Because you will be running on your own localhost or hosting server. Then you will be displayed a code which you can copy and paste at the top of your project.

In your firebase console, goto “Database” tab from left menu and click on “Realtime Database”. For now, you can setup the database in test mode. The code you copied from Firebase console will only have firebase-app.js. But you will also need firebase-database.js in order to use database. We will be using file named “index.php” where all chat operations are performed. Below code has some empty values but when you copy from Firebase console then they will be filled automatically as per your project.

index.php

<!-- The core Firebase JS SDK is always required and must be listed first -->
<script src="https://www.gstatic.com/firebasejs/6.6.1/firebase-app.js"></script>

<!-- include firebase database -->
<script src="https://www.gstatic.com/firebasejs/6.6.1/firebase-database.js"></script>

<script>
	// Your web app's Firebase configuration
	var firebaseConfig = {
		apiKey: "",
		authDomain: "",
		databaseURL: "",
		projectId: "",
		storageBucket: "",
		messagingSenderId: "",
		appId: ""
	};
	// Initialize Firebase
	firebase.initializeApp(firebaseConfig);
</script>

Sending chat message in Firebase

Open your browser console by right click on empty area of page and select “Inspect Element” from context menu and open the “Console” tab. Make sure your internet is connected and check if there is any error. When it comes to sending message, for the sake of simplicity we will be saving username and message in the database. First we need to get the name of user when page opens. We can use Javascript built-in prompt() function to do that. Then we need to create a simple form with an input field to send message. When that form submits then we will call a function that will save the data in Firebase database.

The autocomplete field will make sure not to display old entered values in this field. At the end of sendMessage() make sure to return false. Otherwise the form will submits to itself and the page refreshed. firebase.database() will return an instance of Firebase database. You can either store it in a separate variable to re-use it or you can call it every-time you need to perform some action in database. When the data is saved then Firebase will fire an event that will be called when a new child is added. We will discuss that in details in next step.

The code below will create a new node named “messages”. It will give it a unique ID and save username and message in it. The nodes in database are called as “Childs” in Firebase database.

<script>
	// Your web app's Firebase configuration
	var firebaseConfig = {
		apiKey: "",
		authDomain: "",
		databaseURL: "",
		projectId: "",
		storageBucket: "",
		messagingSenderId: "",
		appId: ""
	};
	// Initialize Firebase
	firebase.initializeApp(firebaseConfig);

	var myName = prompt("Enter your name");
</script>
	
<!-- create a form to send message -->
<form onsubmit="return sendMessage();">
	<input id="message" placeholder="Enter message" autocomplete="off">

	<input type="submit">
</form>
	
<script>
	function sendMessage() {
		// get message
		var message = document.getElementById("message").value;

		// save in database
		firebase.database().ref("messages").push().set({
			"sender": myName,
			"message": message
		});

		// prevent form from submitting
		return false;
	}
</script>

Display messages from chat in Firebase

To display database data from Firebase we need to attach a listener called “child_added”. The main benefit of using this listener is that it simplifies the process of fetching the old messages from database. It is also used to listen to new incoming messages. So this event will be called in 2 situations.

  1. When the page loads it will get all messages saved in database.
  2. And second, when a new child is added in Firebase database.

Code below will create a list where all messages needs to be displayed and attach a listener which will be called automatically when the page loads. On each iteration it will return a snapshot object which is a Firebase database object and it contains child’s key and value.

On each iteration we are creating a new list and appending at the end of UL tag. As the child’s key is unique throughout the database, so we will be adding this as an ID to list item. This will be used when we try to delete message, it will be covered in detail in next step. We will be displaying delete button only if the message is sent from current user which means you can delete only those messages which are sent by you.

Button tag will have an attribute data-id which will be used to get the unique ID of message and an onclick event is attached which will be called when that button is pressed. At the end we are displaying sender’s name and message content.

<!-- create a list -->
<ul id="messages"></ul>
	
<script>
	// listen for incoming messages
	firebase.database().ref("messages").on("child_added", function (snapshot) {
		var html = "";
		// give each message a unique ID
		html += "<li id='message-" + snapshot.key + "'>";
		// show delete button if message is sent by me
		if (snapshot.val().sender == myName) {
			html += "<button data-id='" + snapshot.key + "' onclick='deleteMessage(this);'>";
				html += "Delete";
			html += "</button>";
		}
		html += snapshot.val().sender + ": " + snapshot.val().message;
		html += "</li>";

		document.getElementById("messages").innerHTML += html;
	});
</script>

Delete messages from chat in Firebase

Since we have already created a button to delete message, now we need to create a function that will be called when that button is pressed. We are going to delete the message from database and notify all other users that, that message has been removed. You can either remove that message node from list item or you can simply change the text of message to “This message has been removed” like WhatsApp does.

Next time you refresh the page, that message will be removed altogether. First we are getting the unique ID of message from the data-id attribute of button. Then we call the remove() function of Firebase instance and it will remove that message child from database. As soon as the child is removed, Firebase has an event named “child_removed” which will be called with deleted child key and value. So we are attaching that listener which will return the deleted child key and value which can be used to remove or alter that node from list item.

Now a delete button will be displayed along with your sent messages, when clicked it will be removed from Firebase database and a message will be displayed saying “This message has been removed”.

function deleteMessage(self) {
	// get message ID
	var messageId = self.getAttribute("data-id");

	// delete message
	firebase.database().ref("messages").child(messageId).remove();
}

// attach listener for delete message
firebase.database().ref("messages").on("child_removed", function (snapshot) {
	// remove message node
	document.getElementById("message-" + snapshot.key).innerHTML = "This message has been removed";
});

That’s how you can do real-time chat in Firebase using Javascript. If you do not want to use Firebase and do chat with your own server. You need to follow this tutorial.

Firebase Storage – Upload, Download, and Delete

Private user to user chat in Node JS & MySQL

Private chat does not mean that there is not any server between sender and receiver. There will always be a server in the middle of you and the person you are chatting with. That does not mean that they are spying on your data. It means that they are providing you a medium where you can send and receive messages from others.

It is cost effective since you do not have to pay or worry about the server. A realtime server could take a lot of physical space. It also needs to be air-cooled 24 hours a day, 7 days a week. Also, you need to maintain those servers time-to-time from any failure. You also have to consider all the possible attacks from hackers.

Node JS

Node JS is a Javascript based server that provides realtime event notifications. It is the first thing that came in the mind of developers when they have to develop a private chat. Or even group chat. When you use Facebook, you do not need to refresh the page to see new likes or comments. When you receive a new notification, it will automatically be displayed at the corner left of screen.

Here’s how it works, when you send a message an event will be sent to the server to notify that user. Server will receive that event and send the same event to that specific user. Every user will be listening from server for incoming events. Now, whenever that event received from server a notification will be displayed. You can download and install Node JS from it’s official site.

Setup the project

Create a new folder anywhere in your computer (Node JS can run in any directory). Open that folder in terminal by running command:

cd "path_to_folder"

If you have downloaded and installed Node JS in your computer, you can run the following command to initialize the project:

npm init

You will be asked a series of questions like author, name, description, version etc. You can press enter for all these fields. Then you need to install Express, Http and Socket IO modules. Run the following commands one by one:

npm install express http socket.io
npm install -g nodemon

You do not need to run the last command if you have already installed the “nodemon” module, it helps to keep the server running and the server will restart itself if any change has been made in the server file. The above commands will install the necessary modules to start the server. Next, you are going to need is to download jQuery and Socket IO JS. You can download jQuery from here and Socket IO from here.

Create a new folder named “JS” and paste these files there. Then create a file at the root of your project folder and named it “server.js”, this will be served as your server. Open that file and paste the following code into it:

server.js

// creating express instance
var express = require("express");
var app = express();

// creating http instance
var http = require("http").createServer(app);

// creating socket io instance
var io = require("socket.io")(http);

// start the server
http.listen(3000, function () {
	console.log("Server started");
});

To start the server, open back the terminal and run the following command:

nodemon server.js

This will keep the server running and whenever you make any changes in “server.js” it will automatically restarts itself.

Display connected users

For private chat, the user must select the person he wants to chat with. To do that, create a new file, which will served as a client, named “index.php”. Paste the following code in it:

index.php

<!-- include jquery and socket IO -->
<script src="js/jquery.js"></script>
<script src="js/socket.io.js"></script>

<script>
  // creating io instance
  var io = io("http://localhost:3000");

  var receiver = "";
  var sender = "";

</script>

server.js

io.on("connection", function (socket) {
	console.log("User connected", socket.id);
});

The client will connect with the server and the server will keep listening from the client. As soon as one client connected IO “connection” event will be fired and his socket will be created with a unique ID. Now create a form to enter username and that name will be sent along with each user’s message.

index.php

<form onsubmit="return enterName();">
  <input id="name" placeholder="Enter name">
  <input type="submit">
</form>

<ul id="users"></ul>
	
<script>
	function enterName() {
	    // get username
	    var name = document.getElementById("name").value;

	    // send it to server
	    io.emit("user_connected", name);

	    // save my name in global variable
	    sender = name;

	    // prevent the form from submitting
	    return false;
	}

	// listen from server
	io.on("user_connected", function (username) {
		var html = "";
		html += "<li><button onclick='onUserSelected(this.innerHTML);'>" + username + "</button></li>";

		document.getElementById("users").innerHTML += html;
	});

	function onUserSelected(username) {
	    // save selected user in global variable
	    receiver = username;
	  }
</script>

server.js

var users = [];

io.on("connection", function (socket) {
	console.log("User connected", socket.id);

	// attach incoming listener for new user
	socket.on("user_connected", function (username) {
		// save in array
		users[username] = socket.id;

		// socket ID will be used to send message to individual person

		// notify all connected clients
		io.emit("user_connected", username);
	});
});

Now a form will be displayed where you can enter your name. When form submits, a function named “enterName()” will be called which stored your entered name in a global variable and send an event to the server with that name. The server will listen that event from the client, a new array will be created which holds socket ID of each user identified by his username and send the same event to all connected clients.

Same event is also attached on client side which when called will create a button with username and append it into a list. When that button is pressed, then a function will be called which stores that person’s name in another global variable. These global variables will be used when sending the message.

Send private chat message to selected user

Select a person from the list and now if you open your browser console, you will be able to enter variable “receiver” and it will print out the selected username. Similarly, you can enter “sender” and it will print your name. To send a message you have to follow almost same steps as for displaying connected users.

Create a form, on submit send an event to server. Server will get that person’s socket ID from his username and send an event to that person only. Every user will be listening for that event, whenever that event is received, display the incoming message in a list. After sending message you have to append in the list too so you can see your own messages too.

index.php

<form onsubmit="return sendMessage();">
  <input id="message" placeholder="Enter message">
  <input type="submit">
</form>

<ul id="messages"></ul>
	
<script>
	function sendMessage() {
		// get message
		var message = document.getElementById("message").value;

		// send message to server
		io.emit("send_message", {
		  sender: sender,
		  receiver: receiver,
		  message: message
		});

		// append your own message
		var html = "";
		html += "<li>You said: " + message + "</li>";

		document.getElementById("messages").innerHTML += html;

		// prevent form from submitting
		return false;
	}

	// listen from server
	io.on("new_message", function (data) {
		var html = "";
		html += "<li>" + data.sender + " says: " + data.message + "</li>";

		document.getElementById("messages").innerHTML += html;
	});
</script>

server.js

// listen from client inside IO "connection" event
socket.on("send_message", function (data) {
	// send event to receiver
	var socketId = users[data.receiver];

	io.to(socketId).emit("new_message", data);
});

Open 2 browsers and choose different names for both browsers. Send a message from 1 browser to another and you will see new message on both browsers. You can see that you can chat with each other but when you refresh the page, all previous messages will be removed. That’s because we are not storing any message anywhere, they are just sent and received from one client to another.

When it comes to saving data, you have multiple choices. You can save data in relational database like MySQL, you can save in no-relational database like Mongo DB or you can save in files too in XML or JSON format.

Save messages in MySQL

Saving data in relational database is most easy and reliable among all mentioned above. So create a new database in your phpMyAdmin, you can select any name but for simplicity we have created a database named “web_chat”. Create a table named “messages” and it will have just 4 fields:

  1. ID INT AUTO INCREMENT
  2. sender TEXT
  3. receiver TEXT
  4. message TEXT

Now you need to install a module named “mysql” that will be used by Node JS server. So run the following command in your terminal:

npm install mysql

Don’t forget to start the server again after installing this module: “nodemon server.js”

To use this module in Node JS, first you have to create an instance of this module and create a connection with database. Finally you can connect with your database using connection object. So paste the following lines in your “server.js”:

server.js

// Create instance of mysql
var mysql = require("mysql");

// make a connection
var connection = mysql.createConnection({
	"host": "localhost",
	"user": "root",
	"password": "",
	"database": "web_chat"
});

// connect
connection.connect(function (error) {
	// show error if any
});

To insert a new row in “messages” table, paste the following code in your “send_message” event in “server.js”:

// listen from client
socket.on("send_message", function (data) {
	// send event to receiver
	var socketId = users[data.receiver];

	io.to(socketId).emit("new_message", data);

	// save in database
	connection.query("INSERT INTO messages (sender, receiver, message) VALUES ('" + data.sender + "', '" + data.receiver + "', '" + data.message + "')", function (error, result) {
		//
	});
});

If you try to send a message now, you will see that message in “messages” too. So the INSERT operation works, now we need to show all messages from previous chat when user selected.

Show previous private chat from database

In “index.php” change the “onUserSelected” function to the following:

function onUserSelected(username) {
    // save selected user in global variable
    receiver = username;

    // call an ajax
    $.ajax({
      url: "http://localhost:3000/get_messages",
      method: "POST",
      data: {
        sender: sender,
        receiver: receiver
      },
      success: function (response) {
        console.log(response);

        var messages = JSON.parse(response);
        var html = "";
        
        for (var a = 0; a < messages.length; a++) {
          html += "<li>" + messages[a].sender + " says: " + messages[a].message + "</li>";
        }

        // append in list
        document.getElementById("messages").innerHTML += html;
      }
    });
}

It sends an AJAX request to the server sending the “sender” and “receiver” names as POST parameters, the response received in JSON so the response is parsed in Javascript objects. All messages will looped and appended in the list in the same way as appending in “sendMessage” function or in “send_message” event. In order to setup AJAX requests with POST method on Node JS server, we have to install a module named “body-parser” and we have to tell the Express instance and we will be encoding the URL. So install this module by running the following command in your terminal:

npm install body-parser

server.js

// create body parser instance
var bodyParser = require("body-parser");

// enable URL encoded for POST requests
app.use(bodyParser.urlencoded());

// enable headers required for POST request
app.use(function (request, result, next) {
	result.setHeader("Access-Control-Allow-Origin", "*");
	next();
});

// create api to return all messages
app.post("/get_messages", function (request, result) {
	// get all messages from database
	connection.query("SELECT * FROM messages WHERE (sender = '" + request.body.sender + "' AND receiver = '" + request.body.receiver + "') OR (sender = '" + request.body.receiver + "' AND receiver = '" + request.body.sender + "')", function (error, messages) {
		// response will be in JSON
		result.end(JSON.stringify(messages));
	});
});

Headers will be used to allow your client to send AJAX requests. An API will be created that will fetch all previous messages between you and your selected user and send it back to client in JSON format. And your client is already parsing the JSON and displaying in a list so no need to make further changes in “index.php”.

Using MySQL with async/await

If you are planning to use async in MySQL module, you need to install the module named “mysql2/promise”.

npm install mysql2/promise

After installation, you can include it in your Node JS file.

const mysql = require("mysql2/promise")

const pool = mysql.createPool({
	host: "localhost",
	port: 3306,
	user: "root",
	password: "",
	database: "db_name"
})

const connection = await pool.getConnection()

const [rows, fields] = await connection.query("SELECT * FROM users")

Note: Make sure to make the function async before using await command.

If you want to run the INSERT query and return the ID of the newly inserted row, you can do that by the:

const [result] = await connection.query(`INSERT INTO users (name) VALUES ('Adnan')`)

if (result) {
	const userId = result.insertId
}

Go ahead and give it a free try, the design will not look good obviously, because we haven’t done anything on the design. But the functionality will be good enough for you to understand how private chat system works and how you can send message to specific user even when you have hundreds of users connected at the same time.

Realtime Web-based Chat in Firebase

Realtime Web-based Chat in Node JS & MySQL

We will create a simple and real-time chat module in Node JS and MySQL. We will be using socket IO for real-time communication.

Why chat in Node JS ?

Node JS is a web server that runs under the most famous and widely used programming language, Javascript. As Javascript is asynchronous, so Node JS server can fulfil client requests without having to wait for previous instruction to execute. Node JS runs in non-blocking event, which means that if there is any statement that takes too much time then Node JS will not get stuck at that point rather it will move to the next statement. Node JS can generate dynamic content of a website so user does not have to refresh the page in order to see updated data. This is very useful in realtime applications such as chat apps and google maps etc.

That is why most of the chat apps are developed in Node JS.

Node JS server runs on a single executable file, that file can include other multiple files too. But the server will be executing just the main file. Typically, that file named as “server.js” or “main.js” and that depends if you wanted to have different servers in same folder. That file will be executed just once and then will keep listening to incoming events, as soon it receives a new event it will send it back to other clients. Every time you run the Node JS file, all previous variables, arrays, objects will be destroyed. That is why it is recommended to store that data in some database.

Database for Node JS

When it comes to the database, Node JS has multiple options. You can use MySQL which is famous relational database structure and it is widely used in web development all over the world. Tech giants like Facebook uses MySQL as backend database. Or you can use Mongo DB which is a relation-less data structure, here data is stored in collections and documents. Collection will be name of entity and document is a single record exist in some collection. For example, you will have a collection of user and whenever a new user registered, a new document will be created inside this collection. It is much faster than MySQL.

Or you can use a file system where everything will be saved in file, it is the least recommended method but you can use this for exporting data in external formats. You can have .CSV format which is Comma Separated Values and this type of file usually gets opened in Microsoft Excel (Windows) or Numbers (Macintosh). Similarly, you can use JSON format to store data in files and this type of files holds data in forms of arrays and objects, they are typically used for statistics, for example:

{
    users: [
        { january: 123 },
        { february: 100 },
        { march: 114 },
        { april: 138 }
    ]
}

Installation and setup

First you need to download and install Node JS server in your system. You can download it from it’s official site. After installation, you need to create a folder anywhere in your system and create a file named “server.js”. Make sure the extension of file must be “.js” because the code will be in Javascript. Write the following simple line inside this file, it will show “Hello world !” when the server runs:

server.js

console.log("Hello world !");

Open terminal (CMD) and change the directory to your project folder (cd /path/to/project/). Run the following command in the terminal:

> npm init
> npm install -g nodemon

The first command will start your project folder as Node JS project and it will ask several questions like author (your) name, version etc. Just keep hitting enter if you wanted to skip these. Second command will install nodemon module globally in your system. This is helpful because nodemon allows you to make changes in server file without having to worry to restart the server. Whenever you made any changes in server file, nodemon will automatically restart the server. Otherwise, you have to restart it manually every time you make any change in server file. Now go ahead and run the following command in your terminal, make sure your project’s directory is opened in terminal:

nodemon server.js

When you run the above command, you will see a message saying “Hello world !”. Now install the Express and Http modules in your project by running the following commands, you may need to stop the nodemon running command by hitting Ctrl + C:

> npm install express
> npm install http

After installation you need to create an instance of these modules in your “server.js” file:

// use express
var express = require("express");

// create instance of express
var app = express();

// use http with instance of express
var http = require("http").createServer(app);

To start the server, call the listen function from http object and pass the port number as a parameter. Second parameter is a function that will be called when the server starts running:

// start the server
var port = 3000;
http.listen(port, function () {
    console.log("Listening to port " + port);
});

After pasting these lines in “server.js” you need to run “nodemon server.js” again in the terminal so that the server will keep running on this port.

Socket for incoming connections

Install socket module in your project by running the following command:

> npm install socket.io

And in your “server.js” create an instance of it:

// create socket instance with http
var io = require("socket.io")(http);

To receive incoming connections, you need to attach “connection” event listener on IO object:

// add listener for new connection
io.on("connection", function (socket) {
	// this is socket for each user
	console.log("User connected", socket.id);
});

Whenever a new user connects, this function will be called and we will get it’s socket object in the function argument. We can use the socket ID to send event to specific users. Following is a list of useful functions that you can perform using these IO and socket objects:

EventPurpose
socket.to(socket.id).emit(“name”, “value”)Sends the value to specific user.
io.emit(“name”, “value”)Sends the value to all connected users.
socket.emit(“name”, “value”)Sends the value back to the user who called this event.
socket.broadcast.emit(“name”, “value”)Sends the value to all users except the one who called this event.

Sending chat message event in Node JS

Download and save the socket.io.js file in your project, create a folder named “js” where all JS files will be saved. You can download socket IO from here. Create a file named “index.html” at the root of your project’s folder and include this socket file as Javascript.

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

Then you need to create an instance of socket IO with the same port number as specified in “server.js”. Paste the following code in “index.html”:

<script>
    var server = "http://localhost:3000";
    var io = io(server);
</script>

When you run the index.html in your browser, you will see in your terminal a message for new user connection with a unique socket ID. Paste the following line in your “index.html” inside script tag, it will send the event to server:

// sending message from client
io.emit("new_message", "New message");

And paste the following line in “server.js”, it will receive the event sent from server. This line must be inside IO connection event:

// server should listen from each client via it's socket
socket.on("new_message", function (data) {
	console.log("Client says", data);
});

Run the “nodemon server.js” command again to start the server and refresh the index.html page in your browser and you will see “Client says New message” in your terminal. So now you can create a form where user will enter his desired message and upon hitting submit button, that event will be fired to server. Paste the following code in “index.html” file:

<form onsubmit="return sendMessage();">
	<input id="message" placeholder="Enter message">
	<input type="submit" value="Send">
</form>

<script>
function sendMessage() {
	// get message
	var message = document.getElementById("message");

	// sending message from client
	io.emit("new_message", message.value);

	// this will prevent the form from submitting
	return false;
}
</script>

Emit event to all connected users

Now if you refresh the index.html file, you will see a form with an input field to enter message and a button. Write any message you want and click send button and you will see that message in your terminal window. Now server needs to respond back to all users that a new message has been received. First send the message from server to all connected clients:

// change in server.js inside IO "connection" event
// server should listen from each client via it's socket
socket.on("new_message", function (data) {
	// server will send message to all connected clients
	// send same message back to all users
	io.emit("new_message", data);
});

It will send the same message back to all connected clients. Now all clients must be listening from server for that event, once that event is triggered from server then all clients will show the new message. Paste the following code in “index.html” inside script tag:

// client will listen from server
io.on("new_message", function (data) {
	console.log("Server says", data);
});

Now refresh the page and open a new browser window or you can use different browsers if you have multiple browsers installed in your computer. Then open both browsers side by side. Send message from 1 browser and you will see the message in console of other browser. You can open console by right clicking in empty area and selecting “Inspect Element” option. Create a <ul> tag to display all incoming messages in a list in “index.html”:

<!--list where all messages will be displayed-->
<ul id="messages"></ul>

And paste the following code in the “new_message” event in the “index.html” file:

// client will listen from server
io.on("new_message", function (data) {

    // display message
    console.log("Server says", data);
    
    // creates a new DOM element for li tag
    var li = document.createElement("li");

    // show message in li item
    li.innerHTML = data;

    // append the message at the end of list
    var messages = document.getElementById("messages");
    messages.appendChild(li);
});

Refreshing the page and you will be able to send message from a form and it will be display on the other browser in a list. Send multiple messages and all will be displayed at the end of list. When you refresh the page you will see an empty page because the server only listens to incoming messages. In order to see old messages you can either create an array or store all messages in database. Creating an array will remove all messages when the server restarted. So we are going to save in database.

Connect chat app in Node JS, with MySQL

First you need to install a module named mysql, stop the server and run the following command in your terminal. You can stop the server by pressing Ctrl + C.

npm install mysql

Make sure to run “nodemon server.js” again to start the server.

Create a database in your phpMyAdmin named “web_chat” and inside this database create a table named “messages”. This table can have only 2 fields just for the sake of simplicity, auto increment integer “id” and TEXT “message”. ID will be used when deleting the message. Your table structure should be . Create mysql module instance in “server.js” file and start a new connection with database:

// use mysql
var mysql = require("mysql");

// create connection
var connection = mysql.createConnection({
	host: "localhost",
	user: "root",
	password: "",
	database: "web_chat"
});

connection.connect(function (error) {
	// show error, if any
});

Save messages in MySQL

In “server.js” change the “new_message” event to save the incoming message in database before notifying to all other clients:

// server should listen from each client via it's socket
socket.on("new_message", function (data) {
	console.log("Client says", data);

	// save message in database
	connection.query("INSERT INTO messages (message) VALUES ('" + data + "')", function (error, result) {
		// server will send message to all connected clients
		io.emit("new_message", {
			id: result.insertId,
			message: data
		});
	});
});

This will save the incoming message in database and return the new row inserted ID which can be used to delete the message. Similarly, change your “new_message” event in “index.html” to display the message as:

li.innerHTML = data.message;

If you try to send a message now, you will see that message will also get saved in database table. Now you need to display all previous messages to a client when connected.

Reading messages from MySQL

Download and save the jquery.js file in your project’s js folder. You can download jQuery from it’s official site and include it in your “index.html” file.

<script src="js/jquery.js"></script>

Paste the following code anywhere in the script tag in “index.html”. It sends an API request to Node JS server and display all messages returned from server. Since the data returned from server will be in JSON string, so we have to convert it back to Javascript object by parsing.

$.ajax({
	url: server + "/get_messages",
	method: "GET",
	success: function (response) {
		console.log(response);

		var messages = document.getElementById("messages");

		// parse JSON to javascript object
		var data = JSON.parse(response);
		for (var a = 0; a < data.length; a++) {
			// creates new DOM element
			var li = document.createElement("li");

			// add message content as HTML
			li.innerHTML = data[a].message;

			// append at the end of list
			messages.appendChild(li);
		}
	}
});

Now in “server.js” you need to set header to allow API requests from unknown sources. Since you are testing it from localhost and that is not known for Node JS so you can add a header for “Access-Control-Allow-Origin” and allow all origins by setting it’s value to “*”. Paste the following code outside of IO “connection” event:

// add headers
app.use(function (request, result, next) {
	result.setHeader("Access-Control-Allow-Origin", "*");
	next();
});

next() is a callback function that will attach these headers with incoming API requests. And you need to create an API for “get_messages” that will return all the saved messages in database. Paste the following code in “server.js” outside of IO “connection” event:

// create API for get_message
app.get("/get_messages", function (request, result) {
	connection.query("SELECT * FROM messages", function (error, messages) {
		// return data will be in JSON format
		result.end(JSON.stringify(messages));
	});
});

Since “result.end” can only send string data in response, so you can convert your objects into JSON string and can parse this JSON string back to Javascript objects at client side. Refresh the page and now you will see all previous messages sent in this chat using Node JS API. If you send or receive a new message it will be appended at the end of same list and upon refreshing it will not be removed, because it will already get saved in database.

Delete message

If you have used WhatsApp then you probably know about the delete message feature. Your friend sends you a message and you saw it, then he deletes it and suddenly a message appeared to you “This message has been deleted”. This can be done via Node JS where deleting message from sender will make that message removed from all other users all of a sudden. First you need to create a delete button in 2 functions, when all messages are fetched from database and when a new message has been received. Change the for loop inside “get_messages” AJAX call in “index.html” file:

// give it unique id
li.id = "message-" + data[a].id;

li.innerHTML = data[a].message;

// add delete button
li.innerHTML += "<button data-id='" + data[a].id + "' onclick='deleteMessage(this);'>Delete</button>";

Above code should be pasted inside for loop before appending the child. In your “new_message” event in “index.html”, paste the following code before appending the child:

// same here
li.id = "message-" + data.id;

li.innerHTML = data.message;

// display delete button here too
li.innerHTML += "<button data-id='" + data.id + "' onclick='deleteMessage(this);'>Delete</button>";

Now inside your <script> tag create a new function that will get the message ID and send the delete event to the server with selected message ID:

function deleteMessage(self) {
	// get message id
	var id = self.getAttribute("data-id");

	// send event to server
	io.emit("delete_message", id);
}

In your “server.js” that event will be listened and delete the message from database. After deleting, an event will be sent to all users to remove that message from list. Paste the following code in your IO “connection” event:

// attach listener to server
socket.on("delete_message", function (messageId) {
	// delete from database
	connection.query("DELETE FROM messages WHERE id = '" + messageId + "'", function (error, result) {
		// send event to all users
		io.emit("delete_message", messageId);
	});
});

Now all clients must be listening continuously for delete event and you can remove that list item as soon as user receives that event. We are making it to show “This message has been deleted” similar to WhatsApp when delete event is received from server. So paste the following code in your “index.html” script tag:

// attach listener on client
io.on("delete_message", function (id) {
	// get node by id
	var node = document.getElementById("message-" + id);
	node.innerHTML = "This message has been deleted";
});

Refresh both browsers and try to send message from one browser to anther. You will see a delete button along with message text. When you click that button from one browser, that message will gets deleted from both browsers and instead you will see a message “This message has been deleted”.

Display name with message

At this point, when you send or receive a message you only see the content of message but you also wanted to know WHO sends that message. So first create a form where user can enter his name and then that name will be sent along with his message. In your “index.html” file before send message form, create a simple form like below:

<form method="POST" onsubmit="return setUsername();">
	<input type="text" autocomplete="off" id="username" placeholder="Enter username">
	<input type="submit" value="Select">
</form>

In the same file inside the <script> tag create a function that will create a global variable named “username”:

var username = "";

function setUsername() {
    username = $("#username").val();
	
    // this will prevent the form from submitting	
    return false;
}

Now change the “sendMessage” function to the following so that it will send the username along with message to the server:

function sendMessage() {
	// get message
	var message = document.getElementById("message");

	// sending message from client
    io.emit("new_message", {
        message: message.value,
        username: username
    });

	// this will prevent the form from submitting
	return false;
}

Now in “new_message” event in same file, change the line which append the message content in <li> tag to the following:

li.innerHTML = "" + data.username + ": " + data.message;

Same goes in “get_messages” AJAX call in “index.html”, change the line which append the message content in <li> tag to the following:

li.innerHTML = "" + data[a].username + ": " + data[a].message;

The above line will display the name of user in bold and message in normal style. You can save the username in messages table in database too by first creating a column named “username” and it’s value must be TEXT. Then in “server.js” inside IO “connection” event change the socket’s “new_message” to the following:

// server should listen from each client via it's socket
socket.on("new_message", function (data) {

    // save message in database
	connection.query("INSERT INTO messages(username, message) VALUES('" + data.username + "', '" + data.message + "')", function (error, result) {
		data.id = result.insertId;
		io.emit("new_message", data);
	});
});

Conclusion

Node JS plays a very important role in web development especially in realtime environment. Apart from just creating a chat app in Node JS, you can create a notification system where someone’s activity is monitored realtime by others, or maps that share realtime live location between friends etc. If you wanted to have the chat app in Node JS with the design as above, you can download all source files from the button below. Make sure you download them and let us know if you face any issue.

Single page application in MEVN stack – Realtime chat