Skip to main content

Real time chat server with nodejs socket.io and expressjs

Do you want to get to know about real-time chat server with node.js socket.io and express.js? 

Random chat sites are well popular among people these days, not only among young but also among elder people. Creating a random chat site or an app is an exhausting task and would take several weeks or months to develop something attractive while having a fast API. A chat API is a server-side code that handles user registration and delivering messages between users. One of the hardest parts amateur developers face in creating a chat API is to create the user presence system which broadcasts the information to users about who is online and who has left. Node.js framework is designed to develop live API s which can keep data in variables and let other connections access em without saving the data in a database or saving in a hard drive. And Node.js has a huge community of developers and there are tons of packages that we can install using the node package manager for free which makes it super easy to develop our chat server. In this guide, we will go through the entire server-side code while explaining as much as possible and in the end, we will have a look at how to deploy the code we wrote to a free Heroku server. You will need to install node.js before starting this project and you can do so by downloading the latest node.js framework from this site. In order to connect with this API, you will need socket.io in your project. If it's an app, socket.io for java and flutter is available and for websites, you can use socket.io client package. You will need to have an idea on socket.io and JSON objects to understand this guide properly. But it is highly recommended to try it by yourself even if you are new to node js development.



To begin you will first need to create a node.js project. Create a new folder for the project and open the folder using your favorite IDE. Then create a javascript file called server.js. Then open cmd and change the path to the project folder or open the terminal through the IDE and the path will be automatically set. Then run 'npm init' command. Here is a list of npm commands. It will take you through a process to create the node project. You will be asked several questions and most of em are automatically set and you can just hit enter to continue. After that, you will be able to see a file called package.json and that file contains the package details and starting point of our chat server.

We are going to install two packages using the node package manager. The command to install a package is "node install [pacakge_name]". The two packages we are going to install are,



Express makes it easy to create the server and handle web requests and it is very popular among node js developers. Socket io is a web socket-based package and this handles almost all the functionalities of the server. To install these, simply run "npm install express" and "npm install socket.io" in the terminal. You can see in the package.json file that these two packages are now added as dependencies of the project.


{
  "name": "socket_chat_server",
  "version": "1.0.0",
  "description": "",
  "main": "server.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node server.js"
  },
  "author": "Your name",
  "license": "ISC",
  "dependencies": {
    "express": "^4.17.1",
    "socket.io": "^2.3.0"
  }
}

Now we are ready to start writing the code for our chat server in server.js file. Open the server.js file
and we need to import the express package first. To import a package or module we use the require keyword.

Then we create an app instance by calling the constructor of the express class. After that, we start our server instance by calling the app.listen function which takes two arguments. The first argument is the port in which the server is gonna listen to incoming requests. If you are running this chat server on the localhost for debugging, then you can simply give whatever port you want but it is recommended to use a port number above 1024 as some ports are used by the system. When you deploy the chat server you can set the port as "process.env.PORT" and this will automatically select the best port. The second argument the listen function takes is a callback that will be executed after the server is started. If you need to do some initialization or call some functions when the server is started, then this is the best place to do those method calls. But for this chat app, we really don't need that and we can just simply add a console.log saying that the server is started, so we can see that in the console when we run the chat server in the localhost.


const express = require("express");

const app = express();

let server = app.listen(process.env.PORT, function(){
    console.log("Server started!");
});

app.get('/', function(req,res){
    res.send("server is running!");
}); 

Also, we add a get request handler to the base URL. This will show us whether the server is running or not when we visit the base URL using a browser.

Now the server is started and what's left to do is handle the user registration, user presence and the messaging. Now we gonna use socket.io to implement this. First, we will need several variables to store data. Those are two maps, one to store the socket as the value and the user id as the key, and another one to store the user JSON as the value and the socket as the key. We can use several classes
for this, but for the sake of simplicity, we gonna continue like this. Also, we need two arrays to store new users and left users to broadcast this data to users and this data will be broadcasted in an interval of 5 seconds and we will implement that later. Broadcasting new users and left users is better than sending the whole online user list as if there are many users, it will be always fast and efficient to send those two arrays and sending the whole online user list might affect the server performance and delayed broadcasting due to heavy load of data.


First, we need to create an instance from the socket.io, and the constructor takes a parameter of type server. So passing the server instance we created by calling app.listen needs to be passed into this constructor.


let socketUserMap = new Map();
let userIdSocketMap = new Map();

let newUserArray = new Array();
let leftUserArray = new Array();

const io = require("socket.io")(server);


Now we can start listening to incoming socket connections by using 'on' method in the io instance we created. This function takes two arguments and the first one is the event. To listen to new connections, the event should be 'connection'. The second parameter is a function that will be invoked when a new connection is established. An argument of type socket will be passed into this function when a new user connects. This socket is used to keep the connection with that user. So each user will have his own socket.

Now using this socket, we declare all the events we should listen to and what events to emit. The server is written assuming that there will be three events that users will emit. Those are 'join', 'leave' and 'sendMessage'. 'Disconnect' is automatically called when a user lost his connection to the server.
Join event expects a JSON with user info including a unique id. This id should always be unique as it is used in a map as the key. We add the JSON as an object to the socketUserMap. And the socket with user id to the userIdSocketMap. Then we emit the online user list to that user using the emit method and the values of socketUserMap are used as the online user list. If the user id already exists in the map, then we simply send another event. Also, this user is added to the 'newUserArray' which we gonna use later.

We remove user data from the maps when we receive the leave event from the user. Also, we add that user to the 'leftUserArray'. The disconnect functionality is also the same.

When we receive the sendMessage event we simply forward that message to the correct user using the "receiverId". Because of that, the message JSON should always contain the receiver id.

io.on('connection', function(socket){
    socket.on('join', function(data){
        let parsed = JSON.parse(data);
        if(!userIdSocketMap.has(parsed.id)){
            socketUserMap.set(socket, parsed);
            userIdSocketMap.set(parsed.id,socket);
            newUserArray.push(parsed);
            let users = Array.from(socketUserMap.values());
            let json = {
                'users' : users
            };
            let jsonString = JSON.stringify(json);
            socket.emit('userList', jsonString);
        }else{
            socket.emit('error' , 'already joined!');
        }
    });
    socket.on('disconnect', function(){
        if(socketUserMap.has(socket)){
            let user = socketUserMap.get(socket);
            userIdSocketMap.delete(user.id);
            socketUserMap.delete(socket);
        
            if(newUserArray.includes(user)){
                newUserArray = arrayRemove(newUserArray, user);
            }
            leftUserArray.push(user);
        }
    });
    socket.on('leave', function(){
        if(socketUserMap.has(socket)){
            let user = socketUserMap.get(socket);
            userIdSocketMap.delete(user.id);
            socketUserMap.delete(socket);
        
            if(newUserArray.includes(user)){
                newUserArray = arrayRemove(newUserArray, user);
            }
            leftUserArray.push(user);
        }
    });
    socket.on('sendMessage', function(message){
        let parsed = JSON.parse(message);
        let receiver = parsed.receiver;
        if(userIdSocketMap.has(receiver)){
            userIdSocketMap.get(receiver).emit('message', message);
        }
    });
});

User registration and messaging are now complete. We need a way to broadcast the user presence to online users. The "setInterval" method in javascript suits our needs to broadcast this data in intervals. What we gonna do is simply send a JSON which contains both "newUsersArray" and "leftUsersArray" to every user. We can broadcast this to every user by calling the emit method of the socket.io instance and passing the event as 'broadcast'. This will broadcast the given JSON to all every socket connected to the server. The arrayRemove function is used to remove elements from arrays and this makes sure that there won't be undefined values left in the arrays.

setInterval(() => {
    if(newUserArray.length > 0 || leftUserArray > 0){
    let newJson = JSON.stringify(newUserArray);
    let leftJson = JSON.stringify(leftUserArray);
    let json = {
        'newUsers' : newUserArray,
        'leftUsers' : leftUserArray
    };

    let jsonString = JSON.stringify(json);

    io.emit('broadcast', jsonString);
    newUserArray = new Array();
    leftUserArray = new Array();
}
}, 5000);

function arrayRemove(arr, value) {
    console.log(arr.length);
    let position = arr.indexOf(value);
    arr.splice(position,1);
    console.log(arr.length);
    let f = arr.filter(function (e) {
        return e != null;
    });
    return f;
}

Now our server is ready to deploy. Here is the complete server.js file.


const express = require("express");

const app = express();

let server = app.listen(process.env.PORT, function(){
    console.log("Server started!");
});

const io = require("socket.io")(server);

app.get('/', function(req,res){
    res.send("server is running!");
}); 

let socketUserMap = new Map();
let userIdSocketMap = new Map();

let newUserArray = new Array();
let leftUserArray = new Array();

io.on('connection', function(socket){
    socket.on('join', function(data){
        let parsed = JSON.parse(data);
        if(!userIdSocketMap.has(parsed.id)){
            socketUserMap.set(socket, parsed);
            userIdSocketMap.set(parsed.id,socket);
            newUserArray.push(parsed);
            let users = Array.from(socketUserMap.values());
            let json = {
                'users' : users
            };
            let jsonString = JSON.stringify(json);
            socket.emit('userList', jsonString);
        }else{
            socket.emit('error' , 'already joined!');
        }
    });
    socket.on('disconnect', function(){
        if(socketUserMap.has(socket)){
            let user = socketUserMap.get(socket);
            userIdSocketMap.delete(user.id);
            socketUserMap.delete(socket);
        
            if(newUserArray.includes(user)){
                newUserArray = arrayRemove(newUserArray, user);
            }
            leftUserArray.push(user);
        }
    });
    socket.on('leave', function(){
        if(socketUserMap.has(socket)){
            let user = socketUserMap.get(socket);
            userIdSocketMap.delete(user.id);
            socketUserMap.delete(socket);
        
            if(newUserArray.includes(user)){
                newUserArray = arrayRemove(newUserArray, user);
            }
            leftUserArray.push(user);
        }
    });
    socket.on('sendMessage', function(message){
        let parsed = JSON.parse(message);
        let receiver = parsed.receiver;
        if(userIdSocketMap.has(receiver)){
            userIdSocketMap.get(receiver).emit('message', message);
        }
    });
});

setInterval(() => {
    if(newUserArray.length > 0 || leftUserArray > 0){
    let newJson = JSON.stringify(newUserArray);
    let leftJson = JSON.stringify(leftUserArray);
    let json = {
        'newUsers' : newUserArray,
        'leftUsers' : leftUserArray
    };

    let jsonString = JSON.stringify(json);

    io.emit('broadcast', jsonString);
    newUserArray = new Array();
    leftUserArray = new Array();
}
}, 5000);

function arrayRemove(arr, value) {
    console.log(arr.length);
    let position = arr.indexOf(value);
    arr.splice(position,1);
    console.log(arr.length);
    let f = arr.filter(function (e) {
        return e != null;
    });
    return f;
}

We can deploy any node.js project to a Heroku server for free. You will need to create a Heroku account and install the Heroku CLI on your computer. First, init a git repository in your project folder and commit the changes.

Then run "heroku git:remote -a [your_heroku_app_name]" to set the project. This might prompt you to log in to the Heroku account. After that, you can simply deploy the project by running command "git push heroku master". Now your random chat server is live and you can start developing the client-side project.


If you are unfamiliar or new to node.js or socket.io, then you might be wondering how to use this server on the client-side. Well, That is also very simple as the server. A separate guide on how to implement the client-side will be published soon. Comment below for which platform you want the client side guide. I think you have got a complete idea on real-time chat with node.js socket.io and express.js.

Is there anything that I have missed in this article which you might think which will be important? Comment below and I will add it to the article.

The project is available on Github.

Comments

Popular posts from this blog

How to make a first person character controller - Unity

A first-person character controller script is the starting point of any fps shooter game. Even new Unity game developers tend to write their own fps controller script before learning anything else as it is challenging and very exciting. In this article, we gonna create two simple fps controllers from scratch and will guide you step by step on how it works. First, let's understand the basic concept of an fps controller. You should have a rotating camera that rotates in the x-axis to look up and down and the character should rotate on the y-axis to look around. There are two ways to create an fps controller in the Unity Engine.  Using Character controller component Using the RigidBody component Both have different advantages and disadvantages when using them. It depends on your game to select the best fps controller according to your needs.  Character controller based fps controller Pros: Is grounded check is built-in Has its own methods to move the chara

Unity Get Post web requests - Technoob Technology

Web requests in unity are super easy to use. We gonna use the UnityWebRequest class as the WWW class is deprecated now. UnityWebRequest class can be used for both Get and Post methods. The 'UnityEngine.Networking' namespace is required to work with UnityWebRequests.  This tutorial is made with some simple UI elements. First Let's see how the GET method works. 01. UnityWebRequest.GET We use this method to receive data from the server as normal get requests. Using coroutines in web requests is mandatory as we have to wait until the download is complete before showing the results, and the coroutines make this much easier. A simple button calls the method and a text element will show the result. The script contains a reference to the text element and the string URL of your PHP file.  When the button is pressed the button executes the ButtonGetData method and that method simply starts the GetData coroutine. We use the using keyword as this data

How to make an Advanced Audio Manager for Unity - Technoob Technology

Unity engine gives us a good control on audio clips we use, But the problem is when we add several audio sources for several audio clips it's gonna get hard to control them all. And if you want to make some changes to only one audio source you have to find it first.  This will become a mess if you gonna work with more than 10 audio clips. In this case, we can Create an AudioManager and include all the audio clips in it and make some static methods to play, pause the audio from other scripts. This Audio Manager is written in C# and if you use unity-script you have to convert this into unity script first. In this tutorial, we gonna create an audio manager which contains 7 methods. 1.Play 2.Pause 3.Unpause 4.Stop 5.FadeIn 6.FadeOut 7.lower volume for a duration First, we need a Custom class which contains some strings, floats, bools and an audio clip. This class is not derived from unity mono behavior so we should add [System.Serializable] to mak