Build a Simple Chat App with Node.js and Socket.io
Tags: node, javascript, express
Published: 2020-03-22 14:43:38

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](https://dev-to-uploads.s3.amazonaws.com/i/nditj1kxi36amstixxqj.gif)


## 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:

```bash
mkdir chatApplication && cd client
```

Next, let’s initialize the directory as a Nodejs application.
```bash
npm init -y
```
Now let’s start to install all the dependencies that we’ll need to build this app:

```bash
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.

```bash
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

```javascript
//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!

```javascript
//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:

```html
<!--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. 

```javascript
//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.

```javascript
//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!

```javascript
//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:

```javascript
    //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:

```javascript
    //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 https://github.com/paolodelia99/Simple-node-chat-app no-readme%}