Real-time messaging applications are not a new thing in the web world, but have been gathering more space within the business community, specially in applications that need to integrate with their customers/clients in a more flexible and personal way.

Chats architectures are commonly thought based on a server side application that will support the requests coming from the client (in this case, some JavaScript/Ajax) and deal with the business logic along with database storing, event processing, queues, etc.

When we talk about the front-end solution, all of its magic is based on WebSockets, a communication protocol broadly supported by the browsers with the purpose of establishing a two-way, real-time communication between the browser and the server, enabling faster message exchange without page refresh nor time-consuming wait.

Since not all the versions of browsers (the old ones) support WebSockets, Node.js created a module which takes care of all the transport protocols that can serve as a plan B for the older browsers. They would be the following (in this order): Adobe Flash Socket, Ajax long polling, Ajax multipart streaming, Forever iframe and the JSONP Polling. There are plenty of options, so the module itself is in charge of communicating with the server by the transport protocol that is most convenient.

So in this article we will create a project to get to know the basics of Socket.IO and what it can offer us, without the use of a database, Ajax or another back-end language… only JavaScript, Node.js and jQuery.

About Socket.IO

This Node.js module brings a way to connect directly from the client browser to the application server. The library works through events, that is, the server or client will trigger events so that there are responses from one of the parties, see an example in Figure 1.

Figure 1. Message exchange between client and server.

In a way, let’s use two very basic methods, which are emit and on . One serves to make the emission of the event and the other to receive the response of it. Each side of the application will therefore have the Socket.IO library added.

In addition to allowing the direct exchange of messages between two devices, Socket.IO also allows the broadcast of messages, sending an event to all other connected users. The broadcast can be both from client and server.

When the user accesses the page, a socket is created with the server and it is through this socket that the exchange of messages between a client and a server takes place. This, in turn, can either issue an event to a single Socket or to all the sockets connected to it, what we call a message broadcast.

Setting up the environment

If you do not already have the Node.js environment ready, let’s see together in this section how to get everything ready to start developing such applications. It all comes down to a simple text editor and a terminal (or prompt) for your operating system.

Windows / MAC

Access the official Node.js website and download the msi installation file for your Windows type (32 or 64 bits), or the .pkg file for Mac. Open the file and run the installation normally. At the end, access the terminal (or the command prompt) and run the command node -v . If the answer is the installed version of your Node.js, this means that the environment is ready.

Linux

For Linux, in the Debian and Fedora distributions, the process is quite simple: the installation is performed by the application repository. Let’s look first at the commands to run on the Ubuntu terminal (also valid for Debian distributions), as shown in Listing 1. In Listing 2, we can see the commands for the Fedora distributions.

Listing 1. Commands for installation in Ubuntu (Debian Distributions).

sudo apt-get update

sudo apt-get install Node.js

sudo apt-get install npm

Listing 2. Installation commands for Fedora distributions.



sudo yum -y install Node.js

sudo yum -y install npm sudo curl —silent —location https://rpm.nodesource.com/setup | bash -sudo yum -y install Node.jssudo yum -y install npm

At the end of the process, simply access the terminal and enter the command Node.js -v . If the answer is the installed version of Node.js then our environment is ready to begin.

The second installation command after Node.js refers to the NPM (Node.js Package Manager), a system for package management that is very important in Node.js application development.

The project

Let’s create now a directory called \ChatJs and, inside of it, we will create a file called app.js, which will be the main file of our server. As a first part we will create a fairly simple server that will only present a successful message on the browser screen, as shown in Listing 3.

Listing 3. Creating a single application.

var app = require('http').createServer(response); app.listen(3000);

console.log("App running…"); function response(req, res) {

res.writeHead(200);

res.end("Hi, your server is working!");

}

The script creates an HTTP server (which will be listening on port 3000) which has as main method to be requested the response() function, which, in turn, has two parameters: req (request) and res (response). Into the function, we define a success code (200) and end it with a string warning that the server is ok.

Soon after, just run the following command, which will run our application at the prompt:

node app.js

Note that when you run this code at the prompt, the terminal presents the content of the console.log function warning that the application is running. However, it will not print any other lines, indicating that our application is currently running.

At this point, we have only our Node.js server running. If you access the browser at http://localhost:3000/ you’ll see the message we passed in the end method, as we can see in Figure 2.

Figure 2. Browser response.

Next, we will make our server present an HTML response that will be the main page of our chat. For this, we will have to load the FileSystem module, since we will navigate the project directory and open a file. So, let’s change our app.js just like we see at Listing 4. Before making the changes, go to the prompt and press Ctrl + C (or command + C ) to stop our application on the server.

Listing 4. Introducing an HTML page.

var app = require('http').createServer(response);

var fs = require('fs'); app.listen(3000);

console.log("App running…"); function response(req, res) {

fs.readFile(__dirname + '/index.html',

function (err, data) {

if (err) {

res.writeHead(500);

return res.end('Failed to load file index.html');

} res.writeHead(200);

res.end(data);

});

}

After these changes we will again execute the command node app.js and, when accessing again the address http://localhost:3000/, you’ll come across the message “Error loading the index.html file”, just because we don’t have an index.html file inside our project yet.

It is also important to remember that the server we created so far does not differentiate the path, ie you can put anything after http://localhost:3000/ and it will always respond in the same way because we have not implemented how it’d treat these paths. Soon, you can very well call up addresses like http://localhost:3000/chat, http://localhost:3000/error, http://localhost:3000/potato, etc.

Let’s create a simple interface for our chat. Create an index.html file inside the project root directory. In this file enter a code equal to that shown in Listing 5.

Listing 5. Chat HTML code.

<!DOCTYPE html>

<html>

<head>

<title>ChatJS</title>

<link rel="stylesheet" type="text/css" href="/css/style.css" />

</head>

<body>

<div id="history"></div>

<form id="chat">

<input type="text" id="msg_text" name="msg_text" />

<input type="submit" value="Send!" />

</form>

</body>

</html>

Our index, for now, will only deals with a div called history that is where all the messages exchanged in the chat will be arranged. Then, we have soon after a form with a text box and the button of message sending. A very simple chat structure so far.

However, if you now try to access the address http://localhost:3000/ you will receive the same error message. This is because we do not restart our server application, then we go to the prompt again, press Ctrl + C and then reexecute the app.

As you may have noticed, we already left a link tag in the <head> of our application to load our CSS. Within the directory of our project create another directory called css and, inside it, the style.css file with the same content as shown in Listing 6.

Listing 6. style.css file.

html, body, input {

font-family: Georgia, Tahoma, Arial, sans-serif;

margin: 0;

padding: 0;

} body {

background: #302F31;

padding: 10px;

} form {

margin: 15px 0;

} form input[type='text'] {

border: 2px solid #eb5424;

border-radius: 5px;

padding: 5px;

width: 75%;

} form input[type='submit'] {

background: #eb5424;

border: none;

border-radius: 5px;

color: #FFF;

cursor: pointer;

font-weight: bold;

padding: 7px 5px;

width: 19%;

} #history {

background: #FFF;

border: 2px solid #eb5424;

height: 550px;

}

If we restart the application, the style is not yet applied to the index page. The reason is that our app.js only deals with a request path so far. To solve this we will change our app.js file so that it loads the files that are passed in the request URL, instead of placing each of the URLs manually. Let’s take a closer look at the changes listed in Listing 7.

Listing 7. Path changes in app.js.

var app = require('http').createServer(response);

var fs = require('fs'); app.listen(3000);

console.log("App running..."); function response(req, res) {

var file = "";

if (req.url == "/") {

file = __dirname + '/index.html';

} else {

file = __dirname + req.url;

} fs.readFile(file, function(err, data) {

if (err) {

res.writeHead(404);

return res.end('Page or file not found');

} res.writeHead(200);

res.end(data);

});

}

After restarting the app, you’re going to see the same we have in Figure 3.

Figure 3. Stylized chat page

Sending messages

We will now work on the messaging mechanism. Our application will work by communicating with the Node.js server through the client-side library of Socket.IO while jQuery takes place in the interaction with the page.

For this, we will change the app.js file as shown in Listing 8, and include a line of a command at the beginning of the file stating that we are including Socket.IO in the application.

Listing 8. Including Socket.IO module.

var app = require('http').createServer(response);

var fs = require('fs');

var io = require('socket.io')(app);

…

In order to use the require function in a module we need first to install it for our application. So, stop the application and run the following command to get this done:

npm install socket.io

Once finished, go to your index.html page and add the code snippet shown in Listing 9, at the end of the file.

Listing 9. Message sending event.



<script type="text/javascript" src="

<script type="text/javascript" src="/socket.io/socket.io.js"></script>

<script type="text/javascript">

var socket = io.connect();

$("form#chat").submit(function(e) {

e.preventDefault();



socket.emit("send message", $(this).find("#msg_text").val(), function() {

$("form#chat #msg_text").val("");

});

});

</script>

</body>

</html> https://code.jquery.com/jquery-3.3.1.min.js</a> "> var socket = io.connect();$("form#chat").submit(function(e) {e.preventDefault();socket.emit("send message", $(this).find("#msg_text").val(), function() {$("form#chat #msg_text").val("");});});

We are declaring a socket variable that refers to the Socket.IO library, which will be responsible for all socket functionalities. Next, we declare a submit event of our form in jQuery and pass a preventDefault so that the form does not proceed to its action , since we are the ones who are going to take care of the form response.

Note that the emit method of the library is invoked, in which we pass as parameters three things: the event name (this will be useful on the server), the data we are sending (in this case we are only sending the contents of the message field) and finally the callback , a function that will be executed once the event is issued. The latter, in particular, will only serve to clear the message field, so the user does not have to delete the message after sending it.

If we now test our application the message sending will not work, not even the callback to clear the message field, because we have not yet put the functionality of what the server have to do as soon as it receives this event. To do this, edit the app.js file and put the code shown in Listing 11 at the end of it.

Listing 11. Receiving messages from the client.

io.on("connection", function(socket) {

socket.on("send message", function(sent_msg, callback) {

sent_msg = "[ " + getCurrentDate() + " ]: " + sent_msg;

io.sockets.emit("update messages", sent_msg);

callback();

});

}); function getCurrentDate() {

var currentDate = new Date();

var day = (currentDate.getDate() < 10 ? '0' : '') + currentDate.getDate();

var month = ((currentDate.getMonth() + 1) < 10 ? '0' : '') + (currentDate.getMonth() + 1);

var year = currentDate.getFullYear();

var hour = (currentDate.getHours() < 10 ? '0' : '') + currentDate.getHours();

var minute = (currentDate.getMinutes() < 10 ? '0' : '') + currentDate.getMinutes();

var second = (currentDate.getSeconds() < 10 ? '0' : '') + currentDate.getSeconds();

return year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second;

}

We’ve created a method that will work in response to the client’s connection to the server. When the client accesses the page it triggers this method on the server and when this socket receives a send message we trigger a method that has as parameters the sent data (the message field) and the callback that we created on the client side.

Within this method we put the second part of the functionality: the module will send to the sockets connected to the server (all users) the update messages event and will also pass which new message was sent, with a specific datetime format. To provide the date and time we create a separate function because we will still use this method a few more times throughout the development. Right away, we call the callback that we created on the client side, which is the method for clearing the fields.

Finally, also edit the index.html file and create the method that will update the messages for the users. The idea is quite simple: let’s give an append in the history div (the changes are in Listing 12). The following lines should be entered shortly after submitting the form.

Listing 12. Updating message history.

socket.on("update messages", function(msg){

var final_message = $("<p />").text(msg);

$("#history").append(final_message);

});

Basically, the conversation between the server and the client is the same on both sides, that is, the two have an emit and on functions for issuing and receiving events, respectively.

So, restart and access the application in two tabs and just send a message to see the power of Socket.IO in action. The application should display the message as shown in Figure 4.

Figure 4. Sending and receiving messages.

Conclusion

With this very simple project and a few dependencies, we managed to create, in some lines of code, a very functional application.

Notwithstanding, the power of Socket.IO extends to more solutions that previously relied on back-end pages. Now, the inbound/outbound server response time can rely on the WebSocket protocol for a more agile information exchange, enabling systems that track monitoring results or even setting real-time pricing for users.

You can find the full source code here.