DEV Community

Cover image for Build a Simple Chat App with Node.js and Socket.io
Paolo
Paolo

Posted on

Build a Simple Chat App with Node.js and Socket.io

Are you trying to find a way to build a chat app but you struggles to find a simple tutorial that is gonna to explain everything?

This is the right article for you!

This is a preview of what we’ll building

Alt Text

Disclaimer

In this article I’ll cover the logic that are behind the chat and I won’t cover all the styling part. If you need the css file I’ll put the link of the github repo at the end of the article.

Setup

I assume that you have npm and node installed and knows how they worked (at least the basics). A basic Knowledge of Javascript is required.
So Let’s get started.

Create a directory for the application, open the directory with your favourite editor:

mkdir chatApplication && cd client

Next, let’s initialize the directory as a Nodejs application.

npm init -y

Now let’s start to install all the dependencies that we’ll need to build this app:

npm i express randomcolor socket.io uuid

And as a dev dependency we will install nodemon that is a utility that will monitor for any changes in your source and automatically restart your server.

npm i -D nodemon

Building the Server side

We’ll keep the server side logic in the app.js file( located in the main folder), that is a simple express server

//app.js

const express = require('express');
const app = express();
let randomColor = require('randomcolor');
const uuid = require('uuid');

//middlewares
app.use(express.static('public'));

//routes
app.get('/', (req,res)=>{
    res.sendFile(__dirname + '/client/index.html');
});

//Listen on port 5000
server = app.listen( process.env.PORT || 5000);

Now that we have our server ready, let’s start working on the socket.io logic. First of all we need to instantiate socket.io, after that of io will listen on every connection. When the user is connected our socket will listen to the following events:

  • change_username : get the user username and sending it
  • new_message: listen the new message for the user
  • disconnect: listen when a user is disconnected from the chat

So let's write the code to make it work!

//app.js

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

const users = [];
const connnections = [];

//listen on every connection
io.on('connection', (socket) => {
    console.log('New user connected');
    //add the new socket to the connections array
    connnections.push(socket)
    //initialize a random color for the socket
    let color = randomColor();

    //Set the first username of the user as 'Anonymous'
    socket.username = 'Anonymous';
    socket.color = color;

    //listen on change_username
    socket.on('change_username', data => {
        let id = uuid.v4(); // create a random id for the user
        socket.id = id;
        socket.username = data.nickName;
        users.push({id, username: socket.username, color: socket.color});
        updateUsernames();
    })

    //update Usernames in the client
    const updateUsernames = () => {
        io.sockets.emit('get users',users)
    }

    //listen on new_message
    socket.on('new_message', (data) => {
        //broadcast the new message
        io.sockets.emit('new_message', {message : data.message, username : socket.username,color: socket.color});
    })

    //Disconnect
    socket.on('disconnect', data => {

        if(!socket.username)
            return;
        //find the user and delete from the users list
        let user = undefined;
        for(let i= 0;i<users.length;i++){
            if(users[i].id === socket.id){
                user = users[i];
                break;
            }
        }
        users.splice(user,1);
        //Update the users list
        updateUsernames();
        connnections.splice(connnections.indexOf(socket),1);
    })
})

If you are wondering what randomColor() is for you will find out when will cover the front-end side

Front-end side

So the front-end is pretty easy and its only consists in an html file and two javascript files( and the css file that I’m not gonna cover in this tutorial).
So let’s go in the client folder and create the following file: index.html, chat.js, modalScript.js and the css folder where we gonna keep the style.css.
The index.html when its first render it has a modal to get the user name, and for the other stuff i use flexbox for make it more responsive:

<!--index.html-->

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" const="text/html;charset=UTF-8" />
    <link href="http://fonts.googleapis.com/css?family=Comfortaa" rel="stylesheet" type="text/css">
    <link rel="stylesheet" type="text/css" href="css/style.css" >
    <!--Socket.io scirpt-->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.4/socket.io.js"></script>
    <title>Simple Chat App</title>
</head>

<body>
    <!-- The Modal -->
    <div id="myModal" class="modal">

        <!-- Modal content -->
        <div class="modal-content">
            <h1 class="modal-title">What's your nickname?</h1>
            <input id="nickname-input" class="custom-input" type="text" />
        </div>

    </div>

    <!--Big wrapper-->
    <div class="big-wrapper">
        <!-- Left Column-->
        <div class="online-user-wrapper">
            <div class="online-user-header-container">
                <header>
                    <h2>Online Users</h2>
                </header>
            </div>
            <div>
                <!--Online users goes here-->
                <ul id="users-list">

                </ul>
            </div>
        </div>
        <!--Chat Wrapper -->
        <div class="chat-wrapper">
            <div class="super-chat-title-container">
                <header>
                    <h1>Chat</h1>
                </header>
            </div>

            <!--Messages container-->
            <div id="chatroom">
                <!--x is typing goes here-->
                <div id="feedback"></div>
            </div>

            <!-- Input zone -->
            <div id="input_zone">
                <input id="message" class="vertical-align custom-input" type="text" />
                <button id="send_message" class="vertical-align btn" type="button">Send</button>
            </div>

        </div>
    </div>
    <!--jQuery script-->
    <script src="http://code.jquery.com/jquery-latest.min.js"></script>
    <!--Scripts-->
    <script src="./chat.js"></script>
    <script src="./modalScript.js"></script>
</body>
</html>

If we leave the index.html it as it is the modal won't go away, so we need to add some javascript to handle it.

//modalScript.js

// Get the modal
var modal = document.getElementById("myModal");
const nicknameInput = document.getElementById("nickname-input");

// Close modal when nick-name is typed
nicknameInput.onkeypress = e => {
    let keycode = (e.keyCode ? e.keyCode : e.which);
    if(keycode == '13'){
        modal.style.display = "none";
    }
};

If the backend is listening for events so the frontend have to send these events. We'll send these events thought chat.js file. For instance when a new user enters in the app we have to listen for it and get the username and send it to the backend.

//chat.js file

$(function () {
    //make connection
    let socket = io.connect('http://localhost:5000');

    //buttons and inputs
    let message = $("#message");
    let send_message = $("#send_message");
    let chatroom = $("#chatroom");
    let feedback = $("#feedback");
    let usersList = $("#users-list");
    let nickName = $("#nickname-input");

        //Emit typing
    message.bind("keypress", e => {
        let keycode = (e.keyCode ? e.keyCode : e.which);
        if(keycode != '13'){
            socket.emit('typing')
        }
    });

Messages

As you guessed for the message is the same principle!

//chat.js

$(function () {
    //make connection
    let socket = io.connect('http://localhost:5000');

    //buttons and inputs
    let message = $("#message");
    let send_message = $("#send_message");
    let chatroom = $("#chatroom");
    let feedback = $("#feedback");
    let usersList = $("#users-list");
    let nickName = $("#nickname-input");

    //Emit message
    // If send message btn is clicked
    send_message.click(function(){
        socket.emit('new_message', {message : message.val()})
    });
    // Or if the enter key is pressed
    message.keypress( e => {
        let keycode = (e.keyCode ? e.keyCode : e.which);
        if(keycode == '13'){
            socket.emit('new_message', {message : message.val()})
        }
    })

    //Listen on new_message
    socket.on("new_message", (data) => {
        feedback.html('');
        message.val('');
        //append the new message on the chatroom
        chatroom.append(`
                        <div>
                            <div class="box3 sb14">
                              <p style='color:${data.color}' class="chat-text user-nickname">${data.username}</p>
                              <p class="chat-text" style="color: rgba(0,0,0,0.87)">${data.message}</p>
                            </div>
                        </div>
                        `)
        keepTheChatRoomToTheBottom()
    });

    //Emit a username
    nickName.keypress( e => {
        let keycode = (e.keyCode ? e.keyCode : e.which);
        if(keycode == '13'){
            socket.emit('change_username', {nickName : nickName.val()});
            socket.on('get users', data => {
                let html = '';
                for(let i=0;i<data.length;i++){
                    html += `<li class="list-item" style="color: ${data[i].color}">${data[i].username}</li>`;
                }
                usersList.html(html)
            })
        }
    });

});

// function thats keeps the chatbox stick to the bottom
const keepTheChatRoomToTheBottom = () => {
    const chatroom = document.getElementById('chatroom');
    chatroom.scrollTop = chatroom.scrollHeight - chatroom.clientHeight;
}

Typing

In order to make our chat app complete we need to add a functionality that all the chat apps have: send which user is typing something. To implement it, is quite easy.
In our chat.js file we have to emit when a you are typing and listening when other others users are typing.
Let's add these few lines of code:

    //Emit typing
    message.bind("keypress", e => {
        let keycode = (e.keyCode ? e.keyCode : e.which);
        if(keycode != '13'){
            socket.emit('typing')
        }
    });

    //Listen on typing
    socket.on('typing', (data) => {
        feedback.html("<p><i>" + data.username + " is typing a message..." + "</i></p>")
    });

While in the app.js file we wanna to broadcast we a user is typing. We do it simplying add these three lines of code:

    //listen on typing
    socket.on('typing', data => {
        socket.broadcast.emit('typing',{username: socket.username})
    })

Conclusion

So that’s the end our app. As you can see is pretty simple and it doesn’t require a tons of code as I thought before building it.
Feel free to reports issues and bugs and improve this code, thus making it better.
That’s the Github repo:

GitHub logo paolodelia99 / Simple-node-chat-app

A simple chat app built with node.js and socket.io

Top comments (2)

Collapse
 
hongle profile image
Hong Le

Hi Paolo, thanks for the easy to follow tutorial. I tried your app, followed the steps but was unable to get it to run properly. I also installed the project you have hosted on Github and had similar issues. Just FYI.

Let me know if you need any other info to help resolve this.

Collapse
 
elmardi profile image
Mohamed Elmardi

thank you so much