DEV Community

Cover image for Create a chat app with socket.io and node.js
EneasLari
EneasLari

Posted on

1

Create a chat app with socket.io and node.js

Hello guys,
In this article we are going to create a group chat app that can also be private.

Socket.io

Socket.io is the main package that we are going to use for our web app.
Socket.IO is a library that enables real-time, bidirectional and event-based communication between the browser and the server.

It consists of:

  • a Node.js server
  • a Javascript client library for the browser (which can be also run from Node.js)

Image description

So lets create a server for our web app:

Initialize a new node.js project:
npm init

Install express:
npm install --save express

Install socket.io:
npm install --save socket.io

Install ejs for server side rendering:
npm install --save ejs

The server code:

//Loading dependencies & initializing express
var express = require("express");
var app = express();
var http = require("http");
var socketIO = require("socket.io");
app.use(express.static("public"));
//in order to render a html page for our app
app.get("/", function (req, res) {
res.render("index.ejs");
});
var server = http.createServer(app);
server.listen(process.env.PORT || 3000);
var io = socketIO(server);
io.sockets.on("connection", function (socket) {
//Defining Socket Connections
socket.on("message", function (message, room) {
// for a real app, would be room-only (not broadcast)
socket.to(room).emit("message", message, room);
});
//every new client that will join the chat room will send this event to the server
//and the server will respond acordingly
socket.on("create or join", function (room, clientName) {
var clientsInRoom = io.sockets.adapter.rooms.get(room);
var numClients = clientsInRoom ? clientsInRoom.size : 0;
if (numClients === 0) {
socket.join(room);
socket.emit("created", room, socket.id);
} else {
//this message ("join") will be received only by the previous clients since the last client has not joined the room yet
//send to the clients the new client that will join the room
io.sockets.in(room).emit("join", room, clientName, socket.id); //this client name is the name of the last client who wants to join
socket.join(room);
//this message will be received by all cleints after the join of the last client
io.sockets.in(room).emit("ready");
}
});
//send this info(previous) as a private message to the user that is specified in the parameters(socketid)
socket.on("previousclients", (previous,socketid) => {
// to individual socketid (private message)
io.to(socketid).emit("beforeyou",previous);
});
socket.on("bye", function () {
console.log("received bye");
});
});
view raw server.js hosted with ❤ by GitHub

We use express framework for server, also in order to render a html page we use ejs.

The server handles the messaging between clients. It is the middleware for the message delivering.

The client code:

//Defining some global utility variables
var clientName = "user_" + Math.floor(Math.random() * 1000 + 1);
var remoteclients = [];
var isInitiator = false;
document.getElementById("yourname").innerHTML = "You: " + clientName;
// Prompting for room name:
// var room = prompt('Enter room name:');
//setting test room
var room = "test";
//Initializing socket.io
var socket = io.connect();
//Ask server to add in the room if room name is provided by the user
if (room !== "") {
// socket.emit('create or join', room);
// console.log('Attempted to create or join room', room);
}
//Defining socket events
//Event - Client has created the room i.e. is the first member of the room
socket.on("created", function (room) {
console.log("Created room " + room);
isInitiator = true;
});
//Event - Another client tries to join room
//this message is received only by the client that connected first
//when the second peer is connected
socket.on("join", function (room, client, socketid) {
console.log("Another peer made a request to join room ", room, " whith name :", client);
sendmessagebutton.disabled = false;
//if the client is the creator send to the client that just connected the array of the other users names
if (isInitiator) {
beforeclients = remoteclients.slice(); //or [...remoteclients]
beforeclients.push(clientName);
socket.emit("previousclients", beforeclients, socketid);
}
remoteclients.push(client);
document.getElementById("remotename").innerHTML = remoteclients;
});
//this message will be received by the last client that will connect to the room
//and has the names of all the other users before him, this information is provided by the creator of the room
//as he has all the names of every next client joins the room
socket.on("beforeyou", (previous) => {
remoteclients = previous;
document.getElementById("remotename").innerHTML = remoteclients;
sendmessagebutton.disabled = false;
});
//Event - server asks to log a message
socket.on("log", function (array) {
console.log.apply(console, array);
});
socket.on("message", function (message, room) {
viewmsgtoelement(document.getElementById("messagesent"), message);
});
//Function to send message in a room
function sendMessage(message, room) {
console.log("Client sending message: ", message, room);
socket.emit("message", message, room);
}
//Sending bye if user closes the window
window.onbeforeunload = function () {
sendMessage("bye", room);
};
var connectbutton = document.getElementById("connectbutton");
if (connectbutton) {
connectbutton.addEventListener("click", () => {
if (connectbutton.innerHTML !== "Connected") {
socket.emit("create or join", room, clientName);
}
connectbutton.innerHTML = "Connected";
//connection logic
});
}
let messagetexted = "";
//DOM elements
var messageinput = document.getElementById("messagearea");
if (messageinput) {
//Tip: This event is similar to the onchange event.
//The difference is that the oninput event occurs immediately
// after the value of an element has changed, while onchange occurs
//when the element loses focus, after the content has been changed.
//The other difference is that the onchange event also works on <select> elements.
messageinput.addEventListener("input", (event) => {
messagetexted = event.target.value;
});
}
var sendmessagebutton = document.getElementById("sendmessage");
if (sendmessagebutton) {
sendmessagebutton.disabled = true;
sendmessagebutton.addEventListener("click", () => {
var themessage = "<p>" + clientName + ":" + messagetexted + "</p>";
viewmsgtoelement(document.getElementById("messagesent"), themessage);
sendMessage(themessage, room);
messageinput.value = "";
messagetexted = "";
});
}
function viewmsgtoelement(element, message) {
element.innerHTML += "\n" + message;
}
view raw main.js hosted with ❤ by GitHub

Lets explain the client code

First we define the room name. Here the room name is set to test for simplicity reasons but it can be dynamic. The room name is the name that will need every client in order to join the chat.

  • "created" event, will be received from the first user that will join the room and this client will flagged as the initiator of the room.

  • "create or join" is the event that every user that joins the room will call.

  • "join" event will received from all clients when a new one enters the room and will inform them about his information (in our situation only his name)
    Also in this event the initator will send an event only for he new user that joined the room to update his list of other clients in the room.

  • "beforeyou" event will received only by the last user that will join the room to update his list of the users already existing in the room.

  • "message" event is the main event that every user will receive after the connection in the room is established

The view:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<h1>Send messages peer to peer</h1>
<!-- These paragraphs will be initialized when the connection has been established -->
<p id="yourname"></p>
<p>Other users:</p>
<p id="remotename"></p>
<button id="connectbutton">Connect with peer</button>
<textarea id="messagearea" name="message" rows="5" cols="50"></textarea>
<button id="sendmessage">Send message</button>
<div id="messagesent"></div>
<!-- Import SocketIO for signalling -->
<script src="/socket.io/socket.io.js"></script>
<script src="js/main.js"></script>
</body>
</html>
view raw index.ejs hosted with ❤ by GitHub

The full code on github with more detailed comments:
github

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more