Build a Real-time Chat App with Vue 3, Socket.io and Nodejs

by Solomon Eseme

.

Updated Fri Nov 17 2023

Build a Real-time Chat App with Vue 3, Socket.io and Nodejs

Real-time Chat App with Vue 3 Socket.io and Nodejs follows the previous article I wrote on how to build a real-time chat app with Vuejs, Socket.io, and Nodejs to demonstrate everything you need to set up and create your chat application with Vuejs.

We are going to drive more insight from there to develop a more robust, secure, and advanced Real-time Chat App with Vue 3 Socket.io and Nodejs.

Vue 3 is the most recent version of Vue.js which uses the new composition API, we are going to learn how to set it up and create our first real-time application with Vue 3.

Also, we are going to learn more advanced and real-world concepts of a chat application including features such as authentication and authorization, setting up and storing messages to the database for further analysis.

In this article, we are going to explore in-depth how to build a Real-time Chat App with Vue 3 Socket.io and Nodejs, we’ll also be learning how to add authentication and authorization to our chat application.

Also, we will explore the process of storing and retrieving new and old chat messages from the database in our chat app in real-time.

If this is what you’re looking to achieve, let’s jump in:

Before you dive in, if you’re a backend developer or looking at delving into this career path, join other developers to receive daily articles on backend development that will boost your productivity.

Here is a video of what we will be building.

Build A Real-time Chat App

Setup

Before we get started, we need to make sure that we have the necessary tools installed to get started.

Node.js and NPM are required for this tutorial and it can be easily installed from the official website.

You will also need to have a little knowledge of JavaScript and Vuejs, you can learn VueJS by taking this course and JavaScript with this course also.

If everything is set up properly, let’s get started:

First, create a directory for the application and open the directory with your favorite text editor.

mkdir advanced-chat-app && cd advanced-chat-app && code .

Next, we will initialize the directory with NPM and install the necessary packages needed to build out our real-time chat application.

Run the following command to initialize the project, open your terminal in the project root directory and run:

npm init -y

This will initialize the project with the default NPM set up and create the package.json file so we can start installing our packages.

Dependency Installations

Next, we will start installing our dependencies and setting them up, you can read more about each of the dependencies from the link provided.

First, we will install Express.js and Socket.io using the below command:

npm install express socket.io --save

Lastly, we will install other necessary dependencies (MySQL) using the below command.

npm install mysql2 --save

Database Setup

Persisting the messages of our real-time chat is an added feature to our previous version of this article.

Let’s look at how we can set up our database and start storing and retrieving messages from our database.

First, create a database.js file:

touch database.js

and paste in the following codes:

const mysql = require("mysql2");
let db = null;
class DB {
  constructor() {
    db = mysql.createConnection({
      host: "localhost",
      user: "root",
      password: "DB_PASSWORD",
      database: "advanced-chat-app",
    });
    db.connect(function (err) {
      if (err) console.log(err);
    });
  }

  addUser(data) {
    return new Promise(async (resolve, reject) => {
      if (await this.isUserExist(data)) {
        resolve(true);
      } else
        db.execute(
          "INSERT INTO users (name, user_id) VALUES (?,?)",
          [data.name, data.user_id],
          function (err, rows) {
            if (err) reject(new Error(err));
            else resolve(rows);
          }
        );
    });
  }

  isUserExist(data) {
    return new Promise((resolve, reject) => {
      db.execute(
        "SELECT * FROM users WHERE name = ?",
        [data.name],
        function (err, rows) {
          if (err) reject(new Error(err));
          else resolve(rows[0]);
        }
      );
    });
  }

  fetchUserMessages(data) {
    const messages = [];
    return new Promise((resolve, reject) => {
      db.query(
        "SELECT * from messages where name =?",
        [data.name],
        function (err, rows) {
          if (err) reject(err);
          else resolve(rows);
        }
      );

    });
  }

  storeUserMessage(data) {
    return new Promise((resolve, reject) => {
      db.query(
        "INSERT INTO messages (message, user_id, name) VALUES (?,?,?)",
        [data.message, data.user_id, data.name],
        function (err, rows) {
          if (err) reject(new Error(err));
          else resolve(rows);
        }
      );
    });
  }
}

module.exports = DB;

Server Setup

Now we have our database configuration out of the way, we will set up our Node.js server with express and import our Database module to be used within the events.

Let’s look at how to achieve this:

First, create a server.js file within the directory:

touch server.js

and paste in the following codes.

const app = require("express")();
const http = require("http").Server(app);
const io = require("socket.io")(http);
const path = require("path");
const DataBase = require("./database.js");

const db = new DataBase();

app.get("/", (req, res) => {
  res.sendFile(__dirname + "/index.html");
});
io.on("connection", function (socket) {

  console.log("A user with ID: " + socket.id + " connected");

  socket.on("disconnect", function () {
    console.log("A user with ID: " + socket.id + " disconnected");
  });

  // More Socket listening here.
  if (io.sockets.connected)
    socket.emit("connections", Object.keys(io.sockets.connected).length);
  else socket.emit("connections", 0);

  socket.on("chat-message", async (message) => {
    const data = {
      message: message.message,
      user_id: socket.id,
      name: message.user,
    };
    await db.storeUserMessage(data);
    socket.broadcast.emit("chat-message", message);
  });

  socket.on("typing", (data) => {
    socket.broadcast.emit("typing", data);
  });

  socket.on("stopTyping", () => {
    socket.broadcast.emit("stopTyping");
  });

  socket.on("joined", async (name) => {
    let messageData = null;
    const data = {
      name,
      user_id: socket.id,
    };
    const user = await db.addUser(data);
    if (user) {
      messageData = await db.fetchUserMessages(data);
    }
    socket.broadcast.emit("joined", messageData);
  });

  socket.on("leave", (data) => {
    socket.broadcast.emit("leave", data);
  });

});

http.listen(3000, () => {
  console.log("Listening on port *: 3000");
});

So far, we have created our server using express’ HTTP Server and also initialized our Socket.io using the server instance we created.

We have also set up our homepage endpoint / to display our chat frontend design so we can interact with it.

Next, we created our Socket.io connection and start listening for incoming requests/events e.g. the disconnect event.

We have also made different calls to our databases where needed to either display or store messages in the database.

Lastly, we created the server and listen to incoming requests using port 3000 on localhost.

In my previous article, I explained vividly how socket.io works and the meaning of each method and events we will be using in this tutorial, you should be reading it now if you haven’t already.

Now that we have the server part out of the way, let’s work on our frontend Vue 3 part:

Take a break and subscribe to receive daily information that will boost your productivity as a Nodejs Backend Developer.

Setting up Vue 3 Frontend

Next, we will set up the frontend part of our real time chat app using Vue 3 and Bootstrap.

Vue3 is very easy to get started by adding the CDN to our HTML file for development only:

touch index.html

Open the index.html file and add the following codes:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <link rel="icon" href="/favicon.ico" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>ChatApp_Socket</title>
  <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet"
      integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous" />
</head>
<body>
  <div id="app">
      // Component Code added here
  </div>
</body>
</html>

The code above just includes Bootstrap and Socket.io Client to the frontend.

You can download the client-side Socket.IO library here as well.

Next, we will add the following HTML codes inside <div id="app"> we created above:

<div class="container">
        <div class="col-lg-6 offset-lg-3">
          <div v-if="ready">
            <p v-for="(user, i) in info" :key="i">
              {{ user.username }} {{ user.type }}
            </p>
          </div>
    
          <div v-if="!ready">
            <h4>Enter your username</h4>
            <form @submit.prevent="addUser">
              <div class="form-group row">
                <input
                  type="text"
                  class="form-control col-9"
                  v-model="username"
                  placeholder="Enter username here"
                />
                <input
                  type="submit"
                  value="Join"
                  class="btn btn-sm btn-info ml-1"
                />
              </div>
            </form>
          </div>
          <h2 v-else>{{ username }}</h2>
          <div class="card bg-info" v-if="ready">
            <div class="card-header text-white">
              <h4>
                My Chat App
                <span class="float-right">{{ connections }} connections</span>
              </h4>
            </div>
            <ul class="list-group list-group-flush text-right">
              <small v-if="typing" class="text-white">{{ typing }} is typing</small>
              <li class="list-group-item" v-for="(message, i) in messages" :key="i">
                <span :class="{ 'float-left': message.type === 1 }">
                  {{ message.message }}
                  <small>:{{ message.user }}</small>
                </span>
              </li>
            </ul>
    
            <div class="card-body">
              <form @submit.prevent="send">
                <div class="form-group">
                  <input
                    type="text"
                    class="form-control"
                    v-model="newMessage"
                    placeholder="Enter message here"
                  />
                </div>
              </form>
            </div>
          </div>
        </div>
      </div>

Socket.io Events

Next, we will create the different events to form our basis of communication with the server. The code will go under the comment in the script above.

  <script src="/socket.io/socket.io.js"></script>
  <script src="http://unpkg.com/vue@next"></script>

    <script>
      const socket = io();
      const { createApp, ref, watch, reactive, watchEffect } = Vue;

      const Chat = {
        name: "Chat",

        setup() {
          let newMessage = ref(null);
          let typing = ref(false);
          let ready = ref(false);
          let info = reactive([]);
          let connections = ref(0);
          const messages = reactive([]);
          const username = ref(null);

          window.onbeforeunload = () => {
            socket.emit("leave", username.value);
          };

          socket.on("chat-message", (data) => {
            messages.push({
              message: data.message,
              type: 1,
              user: data.user,
            });
          });

          socket.on("typing", (data) => {
            typing.value = data;
          });

          socket.on("stopTyping", () => {
            typing.value = false;
          });

          socket.on("joined", (data) => {
            info.push({
              username: data.name,
              type: "joined",
            });

            messages.push(...data.messages);

            setTimeout(() => {
              info.length = 0;
            }, 5000);
          });

          socket.on("leave", (data) => {
            info.push({
              username: data,
              type: "left",
            });

            setTimeout(() => {
              info.length = 0;
            }, 5000);
          });

          socket.on("connections", (data) => {
            connections.value = data;
          });

          watch(newMessage, (newMessage, preNewMessage) => {
            newMessage
              ? socket.emit("typing", username.value)
              : socket.emit("stopTyping");
          });

          function send() {
            messages.push({
              message: newMessage.value,
              type: 0,
              user: "Me",
            });

            socket.emit("chat-message", {
              message: newMessage.value,
              user: username.value,
            });
            newMessage.value = null;
          }

          function addUser() {
            ready.value = true;
            socket.emit("joined", username.value);
          }

          return {
            addUser,
            send,
            newMessage,
            messages,
            typing,
            username,
            ready,
            info,
            connections,
          };
        },
      };

      createApp(Chat).mount("#app");
    </script>
  </body>
</html>

The above script uses the on method of the Socket.io to listen for different events from the server and also wait for responses from the server.

Also, the emit method of the Socket.io is used to dispatch or emit events to the server like the POST method in Restful API.

In the same spirit, we listened to different events such as typing, joined, stopTyping etc and response respectively to the different events.

There is a lot happening in that script above, which was explained in my previous article, you should definitely check it out before reading this one.

But here, we will give you a little context of what is happening:

In Vue methods, we have created two methods send() and addUser() methods which basically sends messages to the user using the emit method of the Socket.io and addUser emits the joined event to add that specific user to the chat.

Conclusion

We have learned a lot with build a real-time chat app with Vuejs, socket.io, and Nodejs article.

You might also consider using Sceyt chat API to add chat function in your app without writing a code.

Also, we have learned how to add authentications and add database management to manage all your new changes.

I hope you learned something new with Vue, Node, Express, and Socket.IO. The full code is on GitHub, get it now.

Thank you for reading my article.

Here at my blog or medium I regularly write about backend development, digital marketing, and content management system. To read my future posts simply join my publication or click ‘Follow’ Also feel free to connect with me via Twitter, Facebook, Instagram.

If you enjoy this post, make sure to let us know and share it with your friends.

Whenever you're ready

There are 4 ways we can help you become a great backend engineer:

The MB Platform

Join 1000+ backend engineers learning backend engineering. Build real-world backend projects, learn from expert-vetted courses and roadmaps, track your learnings and set schedules, and solve backend engineering tasks, exercises, and challenges.

The MB Academy

The “MB Academy” is a 6-month intensive Advanced Backend Engineering BootCamp to produce great backend engineers.

Join Backend Weekly

If you like post like this, you will absolutely enjoy our exclusive weekly newsletter, Sharing exclusive backend engineering resources to help you become a great Backend Engineer.

Get Backend Jobs

Find over 2,000+ Tailored International Remote Backend Jobs or Reach 50,000+ backend engineers on the #1 Backend Engineering Job Board

Backend Tips, Every week

Backend Tips, Every week