Chat App with Socket.IO and Express using Node.JS

Chat App with Socket.IO and Express using Node.JS

What will we be used for this project?

Node.JS — JavaScript runtime environment that runs on the V8 engine that has an event-driven architecture capable of asynchronous I/O.
We will be using Node.JS as the environment for our chat app.

Express — Minimal and flexible Node.JS web application framework.

Socket.IO — JavaScript library for realtime web applications. Enables realtime, bi-directional communication between web clients and servers.

Getting Started

Here is a link to a demo and the source code.

Setup Static Site

Create basic HTML file and setup the Express server.

Begin by creating a new project directory.
Create a basic HTML file using the following command:
touch index.html && echo 'hello' > index.html

Next, run the following command to setup the project as a Node.JS application: npm init -y

With the project setup as a Node.JS app, install Express using the following command: npm i express -s
Note: At this point, package.json should include the following:

...
"dependencies": {
"express": "^some.version"
}
...

Create an Express web server that hosts the static index.html file.
Save the following to server.js:

const express = require('express');
const app = express();

app.use(express.static(__dirname));
const server = app.listen(3000, () => {
const { port } = server.address();
console.log(`Listening on port ${port}`);
});

Run: node server.js and visit http://localhost:3000
If you are able to see the word hello in the browser, everything is working as expected.

Frontend

Create display for all messages and the send message form Style using Bootstrap.

Begin by replacing hello on line 1 of index.html with <!DOCTYPE html>.

Next, copy/paste the links for Bootstrap v4 CSS/JS.
Note: The jQuery link from Bootstrap is .slim. Remove this from the link. Also, remove the integrity attribute for the jQuery link.

With Bootstrap and jQuery linked in index.html, create the HTML for the display elements:
Note: Notice the usage of the spacing classes (my-5 and mb-3) provided by Bootstrap v4.

<!DOCTYPE html>

<!-- BOOTSTRAP v4 CDN links -->

<div class="container">
<div class="my-5 jumbotron">
<h1 class="mb-3 display-4">Send Message</h1>
<input class="mb-3 form-control" placeholder="Name" />
<textarea class="mb-3 form-control" placeholder="Message"></textarea>
<button class="btn btn-success">Send</button>
</div>
<div id="messages"></div>
</div>

Send message form display using Bootstrap v4 styles.

Finally, at the bottom of index.html create a script tag to manage the frontend javascript code:

<!DOCTYPE html>

<!-- BOOTSTRAP v4 CDN links -->

<!-- Main Display -->

<script>
$(() => {
console.log('loaded');
});
</script>

If you are able to read loaded upon inspecting the console of the browser then everything is working as expected.

Get Messages Service

Create service that gets the messages from the server and displays them on the frontend.

Begin by creating sample messages:

const messages = [
{name:"Tim",message:"yo"},
{name:"Pam",message:"hi"}
]

Next, expose a GET /messages endpoint:

app.get('/messages', (req, res) => {
res.send(messages);
});

With the GET /messages endpoint created, write a function on the frontend that will request the endpoint and display the messages:

$(() => {
getMessages();
});

function addMessage({name, message}) {
$("#messages").append(`<h4>${name}</h4><p>${message}</p>`);
}

function getMessages() {
$.get("http://localhost:3000/messages", messages => {
messages.forEach(addMessage));
});
}

Displaying the default messages.

If you are seeing results similar to the image above then everything is working as expected.

Post Message Service

Create service that adds a message to messages data storage by clicking the send button.

To begin this section, we must install another module from NPM. body-parser.

By default Express represents what would be JSON data as a String. The body-parser module comes with functions that can be used as Express middleware and allow for JSON to be represented as a Data Object.

To do this run the following command: npm i body-parser -s.
Note: At this point, package.json should include the following:

...
"dependencies": {
"body-parser": "^some.version"
}
...

Import body-parser and setup the Express app to use it to format both JSON and encode the url:

const bodyParser = require('body-parser');

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended:false});

Next, write a POST /message endpoint:

app.post('/message', (req, res) => {
messages.push(req.body);
res.sendStatus(200);
});

With the POST /message endpoint created, update the input, textarea, and button elements to have id attributes as follows:

<input id="name" class="mb-3 form-control" placeholder="Name" />
<textarea id="message" class="mb-3 form-control" placeholder="Message"</textarea>
<button id="send" class="btn btn-success">Send</button>

Write a function on the frontend that will request the POST /message endpoint and bind the function to the click event of the send button:

$(() => {
getMessages();
$("#send").click(() => {
const message = {
name: $("#name").val(),
message: $("#message").val()
}
postMessage(message);
});

function postMessage(message) {
$.post('http://localhost:3000/message', message);
}

Adding your own message.

If you are seeing results similar to the image above and able to submit a message from the browser, reload the page, and view the new message then everything is working as expected.

Socket.IO Setup

Install and setup Socket.IO

Begin by installing Socket.IO npm i socket.io -s.
Note: At this point, package.json should include the following:

...
"dependencies": {
"socket.io": "^some.version"
}
...

The strategy here is to use the Node.JS http module and use it to bind to both Express and Socket.IO to allow a connection to be established:
Note: Notice that the .listen call must now come from http and NOT app.

const http = require('http').Server(app);
const io = require('socket.io')(http);

io.on('connection', () => {
console.log('a user connected');
});

const server = http.listen(3000, () => {
const { port } = server.address();
console.log(`Listening on port ${port}`);
});

With Socket.IO installed and server.js updated, setup the frontend to establish a connection with the server using Socket.IO:

<!DOCTYPE html>

<!-- BOOTSTRAP v4 CDN links -->
<script src="socket.io/socket.io.js"></script>

<!-- Main Display -->

<script>
const socket = io()
// ...top of script tag
</script>

If you are able to see a user connected in the terminal then everything is working as expected.

Emit Socket.IO Message

Emit a message to the frontend when a new message is posted.

This works very well… for just one user. We want this to work for a volume of users and maybe allow for groupings and other features. Something like Slack, HipChat, or Discord.

In order to get the effect we are looking for, when a message is posted Socket.IO must emit a message to the frontend. The frontend will be listening for the emit and have an callback function to respond with.

Begin with updating server.js to emit the message:

app.post('/message', (req, res) => {
messages.push(req.body);
io.emit('message', req.body);
res.sendStatus(200);
});

Next, listen for the emit on the frontend:

socket.on('message', addMessage);

With the message being emitted and added to the display, remove the addMessage call in the postMessage function on the frontend.

If multiple clients are able to send and receive messages in realtime, then the tutorial is complete.

Conclusion

I learned much about Node.JS and Socket.IO while taking the course on LinkedIn Learned as well as writing this article. I hope that someone finds it helpful!
If there are any questions, please leave a comment so we may discuss.