A real time web chat with socket.io
I have recently had fun with this library, and I am documenting my first steps into socketworld hoping this can be helpful. Don’t expect a super advanced tutorial to build the next Telegram, we are dealing with the basics here.
On socket.io website, the “get started” tutorial shows how to build a basic real time web chat in few steps. I will do the same here, trying to cover all the details and adding some more advanced features like a realtime view of the number of connected clients and a basic data persistence so that the application can recognize users when they reload the app.
A live demo of the proof of concept is available here:
And here’s the repository: https://github.com/lamorbidamacchina/easychat
What do you need to get started?
You should have NodeJS already installed on your local machine. If you don’t, start from here.
Using VS Code as code editor is recommended but not compulsory.
At the end of this article I will shorty explain how to deploy the project online using Heroku, a cloud application platform which works great and it’s free (for basic non commercial usage).
Ok, let’s start…
Basic node app setup
First of all, let’s create a new folder where to put our code. I will name mine “mychat”, but you can choose any name you like. Once created, let’s enter inside the folder and run npm init to inizialize a basic NodeJs application.
On macOs, I open Terminal and type
> mkdir mychat
> cd mychat
> npm init
A command line utility will show up, asking you some basic question. You can leave everything empty (press enter), but I suggest you to fill in app.js when the utility asks for entry point.
The name of the entry point is important because we will need to set it up on our production server on Heroku later.
We can now open our code editor. If you use VS Code, we can do it simply typing
> code .
You will notice that now our folder contains a file named package.json. This file contains some basic information about our Node app.
Let’s create a new file app.js and append some basic code to create a simple web server.
To create our server, we rely on Express, which is a useful NodeJS framework, that helps us to create a basic web application with very few lines of codes. By now, we are basically creating a super simple web server listening on port 3001 (or a port specified in a .env file, which we don’t have by now), serving an html file named default.html, located in a /public folder.
Since we are using Express, we need to install it as a dependency in our node project, before starting our web server. To install Express framework, open Terminal and type
> npm install express --save
Once the installation process is over, you will notice that a new folder /node_modules has appeared in our project directory. There is no need to include this folder in our git repository, so I would suggest to create a .gitignore file to avoid versioning /node_modules
Then, let’s create a /public folder, and a default.html file, with just “hello world” written in it.
To start our server easily, we need to edit package.json and add a line under “test”.
This way, once we open Terminal and type npm start, NodeJS will execute app.js and this will start our web server on http://localhost:3001
We can now open our browser on http://localhost:3001 and we should see this:
This means our web server is working correctly, and we can start building our real time chat with socket.io!
For those of you with no experience with Node, Ctrl+c in the Terminal will stop our server.
Add the UI
Let’s create a basic UI for our chat. You can create whatever suites you, but to start I suggest to download the content I put on /public folder, here: https://github.com/lamorbidamacchina/easychat/tree/main/public
Quickly, we have a bootstrap based html template in default.html, linked to an external css file called default.css, and some more js files in /lib folder, basically jQuery and Socket.io libraries. We also need a default.js file, that will manage the client side interactions of our web chat, and that we will analyze further on. By now, just delete all the content in default.js or delete it and create an empty file.
If we run our server again with npm start and point our browser to http://localhost:3001, we should now see something similar to this:
Of course nothing works by now: if you try to send a message or connect from two different devices, you will notice that nothing is changing in our UI.
Add the real time web chat
To make it work, we need to write some code in default.js, to send and receive messages to and form the server, and some more code in app.js, to receive and send messages from and to the clients.
Let’s start with a basic example. Every time some user clicks on the interface, let’s send a “hello world” message to our Socket.io server, and let’s log it.
To do that, we can edit default.js like this:
In app.js, we can edit the code like this:
Now, before restarting our server and check if everything works properly, since we need to add Socket.io dependencies to our project.
In Terminal, we can write:
npm install socket.io --save
and once everything has been installed, let’s restart our server and point to localhost:3001. Now, if I click somewhere on the webchat page, and I check what’s happening to my server, I can see this log:
Two interesting things are happening here. When I open the page, the server can log a new connection (l.10 of app.js). Moreover, whenever the server receives a socket connection named “message”, it displays its content (l.11–13 of app.js).
Where does this socket connection named “message” come from? Easy. It comes from the socket.emit that we have placed in our client side code (l.4 of default.js).
- we can use io.on() to listen to new connection
- we can use socket.emit() to send content through a socket
- we can use socket.on() to read content through a socket
This is basically all we need to know to build our basic real time chat, so let’s have a look at a more advanced version of both client and server side code.
Here’s the new version of default.js:
- we save a random id for every new user, storing it in local storage (l.6–12). later we could update the code to manage a nickname in a similar way.
- we send the value of the message field to the “message” socket, together with the user id (l.14–22)
- we listen to “message” socket, and we update our chat board every time we receive a new message from a user, adding a timestamp (l-24–31)
- we listen to “participants” socket, and update a counter on top of the page
Now let’s see what’s happening on the server side, in the new version of app.js:
Things to point out on app.js:
- we can read the number of connected clients every time a new client connects, and send it to all the clients through a socket named “participants” (l.12–14)
- we can read any message sent through “message” socket, log it and send it back to all the connected clients (l.17–20)
- we can read the number of connected clients every time a client disconnects, and send it to all the clients through “participants” socket (l.24–29)
If you restart your server and connect to https://localhost:3001 from two distinct browsers, you should totally see our real time web chat up and running :)
Let’s split our chat into different chatrooms
With socket.io it’s pretty easy to set up different rooms, which basically are different channels that our sockets can join or leave separately.
Let’s suppose we want to create different chatrooms by simply adding a parameter to our url: http://localhost:3001/?room=white should bring us to a chatroom whose name is “white”, for example.
Here’s the new version of default.js ( I also added some code to assign different random colors to the random usernames of the users btw)
First, we need to read the “room” parameter from the url (lines 6–8), and pass it to our server via an .emit event (line 22).
Then, every time we submit a new message to our server, we should send an information about which room we are going to send the message to (line 39)
Server side, here’s the updated version of app.js
First of all we read the content of a new socket called “room”, log it in console and call socket.join() to subscribe the socket to the given room (lines 12–15).
Now let’s examine lines 25–29. On “message” socket we read a new property of the object that we have just set up in our client side code — obj.room. Instead of broadcasting the received message to all the connected clients, we send the message only to the given room, using io.to(name-of-the-room).emit().
Now, to make our web chat a little more useful, we should publish the project to make it reachable to everyone from everywhere
Publish on Heroku
Heroku offers a free cloud service where we can easily host our application. Once signed up and logged in, you can create a new app:
Once your new app is set up with a name and a region, you should download and install Heroku CLI.
Once you have successfully installed Heroku CLI on your machine, you can open Terminal and type
You should see this message:
Once logged in, you should type this command, where NAME_OF_THE_APP is the name you have previously chosen for your app on Heroku:
heroku features:enable http-session-affinity -a NAME_OF_THE_APP
As stated in their documentation page, this command enables sticky sessions, which we need to make our chat works on Heroku.
Once this feature is enabled on our app, we only need to deploy our code to Heroku servers. To do that, you can open your app from Heroku’s dashboard, and click on Deploy tab. There you can choose your preferred deployment method.
I choose to connect my GitHub, where I have previously created a repository named “easychat”, to my app on Heroku.
If you don’t want to set up your repository on Heroku, you can choose another option from the Deployment method panel.
If you scroll down the same page, you can choose whether to enable automatic deploys every time a new push to Main branch is performed, or to disable it and manage your deploys manually.
Clicking on Open App on top of the same page, you will be able to see your real time web chat up and running, and completely accessible to everyone from everywhere.
Of course this is just a proof of concept which needs further refinements to make it a usable and interesting prototype.
Here’s a short list of add ons which I would like to develop whenever I have time:
- Add customizable nicknames and colors instead of random strings for users
- Add emoticons
- Add a feature to create different chat rooms
- Add one to one chatrooms
- Add end to end encryption