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

10 Replies to “Private user to user chat in Node JS & MySQL”

      1. Im registered bro, and have the same problem, it only shows the second part, the sign up part for the users does not appear and I can not add users from de button ‘+’, great tutorial, can you help me? THANKS

  1. body-parser deprecated undefined extended: provide extended option server.js:14:20

Comments are closed.