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,
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.
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.
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.
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.
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.
Now our server is ready to deploy. Here is the complete server.js file.
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.
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.
The project is available on Github.
Comments
Post a Comment