Real-time Chat Application Using Strapi, Next, Socket.io, and PostgreSQL
Tags: strapi, nextjs, postgresql, tutorial
Published: 2022-06-28 08:27:06

---
canonical_url: https://strapi.io/blog/real-time-chat-application-using-strapi-next-socket-io-and-postgre-sql?utm_campaign=Strapi%20Blog&utm_source=devto&utm_medium=blog
---

_In this tutorial, you’ll build a chat application hovering around authentication and SQL database._

**Author: Fredrick Emmanuel**

Sending and receiving text messages in recent times are frequently done online and in real-time. Text messages in real-time chat applications are sent and received immediately, depending on the speed of the router. Realtime messaging is very essential in chat applications, and [Socket.io](https://socket.io) grants us this privilege.

## Goal
In this tutorial, you’ll build a chat application hovering around:
- Authentication [Email Auth, Token Auth, and Role Auth]
- SQL database (PostgreSQL)

## Prerequisites
To follow effectively with this tutorial, be sure you have:
- [Node version 16.4+](https://nodejs.org/en/download/) installed,
- Basic knowledge of NextJS,
- [PostgreSQL](https://www.postgresql.org/download/) installed, and
- Text Editor, [Vs code](https://code.visualstudio.com/download) preferably.
> This article is a modification of [How to Build a Real-time Chat Forum using Strapi, Socket.io, React, and MongoDB](https://strapi.io/blog/how-to-build-a-real-time-chat-forum-using-strapi-socket-io-react-and-mongo-db). 

Now that all is correct, let’s set up the PostgreSQL database.

## Setting up PostgreSQL
Search and open up *PgAdmin* in the start menu of your computer. *PgAdmin* will help create and manage your *Strapi database.*
> PgAdmin is installed when you install [PostgreSQL](https://www.postgresql.org/download/). 
* When PgAdmin opens, you will be prompted to put in the password entered during the installation.

![PyAdmin Screenshot](https://paper-attachments.dropbox.com/s_2AB6CD6238DB99E473ED13F1FBA90C2266A685066D162C9FB97895FCD9D8F400_1648245416520_PgAdmin+Password.jpg)

* On the left navigation bar, click on **Servers** and click on **PostgreSQL** **14**.

![A Sample Screenshot](https://paper-attachments.dropbox.com/s_2AB6CD6238DB99E473ED13F1FBA90C2266A685066D162C9FB97895FCD9D8F400_1648257844042_PgAdmin+clicks.jpg)

* Right-click on **Databases,** hover over **Create** and click on **Database**.

![A Sample Screenshot](https://paper-attachments.dropbox.com/s_2AB6CD6238DB99E473ED13F1FBA90C2266A685066D162C9FB97895FCD9D8F400_1648259457129_PgAdmin+Rightclick.jpg)

* You can name the database anything you desire, but, in this tutorial, the name of the database is **chatapp**. Once you're done naming the database, hit save.

![A Sample Screenshot](https://paper-attachments.dropbox.com/s_2AB6CD6238DB99E473ED13F1FBA90C2266A685066D162C9FB97895FCD9D8F400_1648259580207_creating+database.jpg)


The name of the database, **chatapp**, will be shown on the left navigation bar as shown below:

![A Sample Screenshot](https://paper-attachments.dropbox.com/s_2AB6CD6238DB99E473ED13F1FBA90C2266A685066D162C9FB97895FCD9D8F400_1648261098474_created+database.jpg)

## Connecting PostgreSQL with Strapi
In this last section, you learned how to create a database in PostgreSQL. In this section, you’ll learn how to connect PostgreSQL with Strapi using the **Quickstart** option and the **Custom setup** option.

## Custom Setup
Open up your terminal and create a folder for this application. This folder is the project’s root directory for this application. In this tutorial, the name of this folder is **strapified-chat.** Not a bad name, right? 
```
     mkdir strapified-chat
```
Open the folder in your desired code editor or run the code below in your terminal to open the folder with Vs code:
```bash
    cd strapified-chat
    code .
```
After starting your code editor, open the integrated terminal in your code editor to quickly initialize and create a *package.json* file for the project using the code below:
```bash
    npm init --y
```
Now, run the command below to create the Strapi app. You can give the Strapi app the name you desire but, in this tutorial, the name of the strapi app is *chatapp.*
```bash
    npx create-strapi-app chatapp
```
Executing the `create-strapi-app` command will bring you to an option shown below:

![A Sample Screenshot](https://paper-attachments.dropbox.com/s_2AB6CD6238DB99E473ED13F1FBA90C2266A685066D162C9FB97895FCD9D8F400_1648265636624_select+option.jpg)

Use your **down arrow** key to select **Custom** and hit `Enter`. Selecting **Custom** allows you to choose the database you want and a few other settings. Navigate using your arrow keys, select **postgres,** and hit `Enter`**:**

![A Sample Screenshot](https://paper-attachments.dropbox.com/s_2AB6CD6238DB99E473ED13F1FBA90C2266A685066D162C9FB97895FCD9D8F400_1648266103530_choosing+database.jpg)

Since the Strapi app and database have the same name, **chatapp**, just hit `Enter` to move on. 

> If `chatapp` is not the name of your PostgreSQL database, specify the name of the database and hit `Enter`.

Press `Enter` to leave **Host** and **Port** as default. Key in `postgres` as the **Username**. `postgres` is the default name assigned when creating the database.

![A Sample Screenshot](https://paper-attachments.dropbox.com/s_2AB6CD6238DB99E473ED13F1FBA90C2266A685066D162C9FB97895FCD9D8F400_1648267541729_username.jpg)

Enter the **password** to your database:

![A Sample Screenshot](https://paper-attachments.dropbox.com/s_2AB6CD6238DB99E473ED13F1FBA90C2266A685066D162C9FB97895FCD9D8F400_1648267793723_password.jpg)

Enter `n` to disable SSL connection.

![A Sample Screenshot](https://paper-attachments.dropbox.com/s_2AB6CD6238DB99E473ED13F1FBA90C2266A685066D162C9FB97895FCD9D8F400_1648327201065_creating+project.jpg)

Once the series of questions are over, Strapi creates the project with the custom options you specified. When the installation is done, Strapi sends a success message and outlines the basic commands that help get your project started.

![A Sample Screenshot](https://paper-attachments.dropbox.com/s_2AB6CD6238DB99E473ED13F1FBA90C2266A685066D162C9FB97895FCD9D8F400_1648330157064_created+strapi.jpg)

#### Accessing Strapi Admin
Now that you have installed Strapi into the application, it’s time to get access to Strapi admin. Move into the created project and run the `strapi build command` below to build the Strapi admin UI:
```bash
    cd chatapp
    npm run build
```
The output is shown below:

![A Sample Screenshot](https://paper-attachments.dropbox.com/s_2AB6CD6238DB99E473ED13F1FBA90C2266A685066D162C9FB97895FCD9D8F400_1648396392133_strapi+build.jpg)

Now, run the `strapi develop command` below to start up Strapi in development mode:
```bash
    npm run develop
```

#### Quickstart
If the above step worked for you, kindly skip to the next section or stick around to gain more knowledge. 
* Create a folder for this project. You can decide to name it anything you want but in this tutorial, its name is **strapified-chat.**
* Open this folder in your terminal and run the `create-strapi-app` command below. This will create a Strapi app named `chatapp`:
```bash
    npx create-strapi-app chatapp --quickstart
```

The **quickstart** option automatically builds the admin panel and runs Strapi in **development** mode.

#### Configuring Strapi
* Once the installation is complete, open the folder named **strapified-chat** in a code editor. 
* Click on **chatapp** then select **database.js** from the **config** folder.

![A Sample Screenshot](https://paper-attachments.dropbox.com/s_2AB6CD6238DB99E473ED13F1FBA90C2266A685066D162C9FB97895FCD9D8F400_1648517670015_database+folder+structure.jpg)

* Replace the following code into the **database.js** file to configure the PostgreSQL database.

```js
    module.exports = ({ env }) => ({
      connection: {
        client: "postgres",
        connection: {
          host: env("DATABASE_HOST", "127.0.0.1"),
          port: env.int("DATABASE_PORT", 5432),
          database: env("DATABASE_NAME", "chatapp"),//Name of database
          user: env("DATABASE_USERNAME", "postgres"),//Default username
          password: env("DATABASE_PASSWORD", "12345678"),//Password to your PostgreSQL database
          ssl: env.bool("DATABASE_SSL", false),
        },
      },
    });
```

Once you’re done inputing the code above in your **database.js** file, hit save and let Strapi restart the server.

#### Setting up NextJS
After connecting PostgreSQL with Strapi, the next step is to install NextJS into the *Chat* application. 
* Run the `create-next` app command below to move out from the Strapi folder and spin up the next application in a new folder.
```bash
    cd ..
    npx create-next-app next-chat
```
* Follow the prompt to install **create-next-app**.

![A Sample Screenshot](https://paper-attachments.dropbox.com/s_2AB6CD6238DB99E473ED13F1FBA90C2266A685066D162C9FB97895FCD9D8F400_1648928608601_create-next-app+prompt.jpg)

## Email Authentication using Nodemailer
[Nodemailer is a NodeJS module that enables you to send emails from your email server.](https://nodemailer.com/about/) Nodemailer makes use of the credential of your email service to send these emails. In this section, you will learn how to set up Nodemailer for your different email services.
Now, open your terminal in the *next-chat* directory and install nodemailer.
```bash
    cd next-chat
    npm i nodemailer
```

Below is the output of the installation of nodemailer:

![A Sample Screenshot](https://paper-attachments.dropbox.com/s_2AB6CD6238DB99E473ED13F1FBA90C2266A685066D162C9FB97895FCD9D8F400_1651846802018_nodemailer+installation.jpg)

#### Configuring Nodemailer
Once the installation of *Nodemailer* is complete, navigate to **pages/api** in the *next-chat* project directory and create a file named *mail.js.*

![A Sample Screenshot](https://paper-attachments.dropbox.com/s_2AB6CD6238DB99E473ED13F1FBA90C2266A685066D162C9FB97895FCD9D8F400_1651853165774_mail.js+creation.jpg)

To send an email using nodemailer requires an email service. This tutorial makes use of the *Google Mail* service (Gmail), but you can choose a different email or SMTP service.

> To make use of the Google Mailing Service with nodemailer, you need to [allow access to a less secured app.](https://myaccount.google.com/lesssecureapps)

* Open the *mail.js* file and add the following code to configure nodemailer.
```js
    export default function (req, res) {
      const nodemailer = require("nodemailer");
      const transporter = nodemailer.createTransport({
        port: 465,
        host: "smtp.gmail.com",
        secure: "true",
        auth: {
          user: "examaple@gmail.com",//Replace with your email address
          pass: "example",// Replace with the password to your email.
        },
      });
      const mailData = {
        from: "Chat API",
        to: req.body.email,
        subject: `Verify your email`,
        text: req.body.message,
      };
      transporter.sendMail(mailData, function (err, info) {
        if (err)
          return res.status(500).json({ message: `an error occurred ${err}` });
        res.status(200).json({ message: info }); de
      });
    }
```

#### Setting up the Login Form
Now that nodemailer has been configured, let's create a simple login form that will accept the email from the user.

* Open up the *index.js* file in the **pages** folder and replace the code in it with the following code.
```js
    import styles from "../styles/Home.module.css";
    export default function Home() {
      return (
        <div className={styles.container}>
          <form className={styles.main}>
            <h1>Login</h1>
            <label htmlFor="name">Email: </label>
            <input type="email" id="name" />
            <br />
            <input type="submit" />
          </form>
        </div>
      );
    }
```    

After refactoring the index.js file and creating a simple form, the next step is to add functionality.

#### Getting the Email from the User
1. Import the *useState* dependency from *react* in the *index.js* file.
    import { useState } from "react";
2. Add an *onChange* event that will listen for any change in the input and store it in a state variable.
```js
    export default function Home() {
      const [email, setEmail] = useState("");
      const [user, setUser] = useState("");
      return (
        <div className={styles.container}>
          <form className={styles.main}>
            <h1>Login</h1>
            <label htmlFor="user">Username: </label>
            <input
              type="text"
              id="user"
              value={user}
              onChange={(e) => setUser(e.target.value)} // Getting the inputs
            />
            <br />
            <label htmlFor="name">Email: </label>
            <input
              type="email"
              id="name"
              onChange={(e) => setEmail(e.target.value)} // Getting the inputs
            />
            <br />
            <input type="submit" />
          </form>
        </div>
      );
    }
```

3. Now, create a function that will get the email when the user clicks submit and sends a POST to `api/mail`.
```js
    export default function Home() {
      const [email, setEmail] = useState("");
      const [user, setUser] = useState("");
      const handlesubmit = (e) => {
        e.preventDefault();
        let message = "Testing, Testing..... It works🙂";
        let data = {
          email, // User's email
          message,
        };
        fetch("/api/mail", {
          method: "POST",  // POST request to /api//mail
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify(data),
        }).then(async (res) => {
          if (res.status === 200) {
            console.log(await res.json());
          } else {
            console.log(await res.json());
          }
        });
        setEmail("");
        setUser("");
      };
      return (
        <div className={styles.container}>
          <form className={styles.main}>
            <h1>Login</h1>
            <label htmlFor="user">Username: </label>
            <input
              type="text"
              id="user"
              value={user}
              onChange={(e) => setUser(e.target.value)} // Getting the inputs
            />{" "}
            <br />
            <label htmlFor="name">Email: </label>
            <input
              type="email"
              id="name"
              value={email}
              onChange={(e) => setEmail(e.target.value)} // Getting the inputs
            />
            <br />
            <input type="submit" onClick={handlesubmit} /> // Handling Submit
          </form>
        </div>
      );
    }
```

Once you are done with adding the code above and hitting save, head on to [localhost:3000](http://localhost:3000) to try out the form.

## Token Authentication using JWT
According to the documentation, JSON Web Token (JWT) is an open standard ([RFC 7519](https://tools.ietf.org/html/rfc7519)) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. JWT generates a unique token that can authenticate and authorize a user.

#### Adding JWT to the Application
1. Open up your terminal in the *next-chat* folder and install the JSON Web Token dependency.
```bash
    npm i jsonwebtoken
```
The output is shown below.
![A Sample Screenshot](https://paper-attachments.dropbox.com/s_2AB6CD6238DB99E473ED13F1FBA90C2266A685066D162C9FB97895FCD9D8F400_1651927374413_JWT+install.jpg)
2. Add the following code to the `handsubmit` function in the *index.js* file.
```js
     const handlesubmit = (e) => {
        e.preventDefault();
        const id = Math.trunc(Math.random() * 1000000); //Generating a random number and taking the first 10 numbers.
        let account = { // Creating the payload
        id
        };
        const SECRET = "this is a secret"; //JWT secret
        const token = jwt.sign(account, SECRET);// Creates the Token
        console.log(token);
      };
```
> For security purposes, the `SECRET` variable for JWT should be a random set of strings and should be stored in an environmental variable.

## Setting up Strapi

[Strapi is an open-source headless CMS that gives developers the freedom to choose their favorite tools and frameworks and allows editors to manage and distribute their content using their application's admin panel.](https://strapi.io/) Now that you’ve generated the token using JWT, it’s time to store the username, email, and token using my favorite CMS, **Strapi**.

1. Ensure that your **Strapi** server is up and running.
```bash
    cd chatapp
    npm run develop
```
![A Sample Screenshot](https://paper-attachments.dropbox.com/s_2AB6CD6238DB99E473ED13F1FBA90C2266A685066D162C9FB97895FCD9D8F400_1651957851970_strapi+develop.jpg)

2. Navigate to [localhost:1337/admin](http://localhost:1337/admin) and log in with your Strapi admin credentials.
![A Sample Screenshot](https://paper-attachments.dropbox.com/s_2AB6CD6238DB99E473ED13F1FBA90C2266A685066D162C9FB97895FCD9D8F400_1651958109235_strapi+admin.jpg)

3. After a successful login, click on **Content-Type Builder** on the side-nav bar and *click on* **Create new collection type***.*
![A Sample Screenshot](https://paper-attachments.dropbox.com/s_2AB6CD6238DB99E473ED13F1FBA90C2266A685066D162C9FB97895FCD9D8F400_1651958557687_strapi+nav-bar.jpg)

4. Give it a name of your choice, but in this tutorial, the name of the collection type is **Account**.
![A Sample Screenshot](https://paper-attachments.dropbox.com/s_2AB6CD6238DB99E473ED13F1FBA90C2266A685066D162C9FB97895FCD9D8F400_1651959225930_Account.jpg)

5. After naming the collection type, hit continue, choose **Text**, and name it **username.**
![A Sample Screenshot](https://paper-attachments.dropbox.com/s_2AB6CD6238DB99E473ED13F1FBA90C2266A685066D162C9FB97895FCD9D8F400_1651959722686_usernnname.jpg)

6. Click on **Add another field** to configure the field for email and token. Choose the Email field for Email and Text field for Token, then hit finish. The output is shown below
![A Sample Screenshot](https://paper-attachments.dropbox.com/s_2AB6CD6238DB99E473ED13F1FBA90C2266A685066D162C9FB97895FCD9D8F400_1651960432643_Account+field.jpg)

7. Click on *Save* at the top-right of the screen and let Strapi restart the server.
![A Sample Screenshot](https://paper-attachments.dropbox.com/s_2AB6CD6238DB99E473ED13F1FBA90C2266A685066D162C9FB97895FCD9D8F400_1651960882780_strapi+restart.jpg)

## Permissions in Strapi
To increase security, Strapi blocks all CRUD requests to a newly-created collection type by default. The user’s credential needs to be sent to the **Account** collection type, so this feature will not be needed in this application.

1. To allow requests, click on **Settings** on the side-nav bar and click on **Roles** under **USERS & PERMISSIONS PLUGIN**.
![](https://paper-attachments.dropbox.com/s_2AB6CD6238DB99E473ED13F1FBA90C2266A685066D162C9FB97895FCD9D8F400_1652025229291_strapi+role.jpg)

2. Scroll down to **Accounts**, click on the drop-down, **Select all** and hit **Save** at the top-right of the screen.
![](https://paper-attachments.dropbox.com/s_2AB6CD6238DB99E473ED13F1FBA90C2266A685066D162C9FB97895FCD9D8F400_1652026108465_strapi+permissions.jpg)

#### Storing the User’s Credentials

Now that you’ve learned how to edit the permission of a collection type, it’s time you learn how to store the user’s details in that collection type using **Strapi.**

1. Make sure your NextJS application is running.
```bash
    cd next-chat
    npm run dev
```
![A Sample Screenshot](https://paper-attachments.dropbox.com/s_2AB6CD6238DB99E473ED13F1FBA90C2266A685066D162C9FB97895FCD9D8F400_1652037569967_next+dev.jpg)

2. The next step is to make a post request to [http://localhost:1337/api/accounts](http://localhost:1337/api/accounts), passing the credentials of the user as a parameter.

Open your *index.js* file in the *pages* folder and add the following code to the `handlesubmit` function.
```js
    const handlesubmit = (e) => {
        e.preventDefault();
        let message = "Testing, Testing..... It works🙂";
        const id = Math.trunc(Math.random() * 1000000); //Generating a random number and taking the first 10 numbers.
        let data = {
          email, // User's email
          message,
        };
        let account = {
         id
        };
        const SECRET = "this is a secret";
        const token = jwt.sign(account, SECRET);
        let strapiData = { // Parameters for Strapi
          data: {
            id,
            username: user,
            email,
            token,
          },
        };
        fetch("http://localhost:1337/api/accounts", {
          method: "POST",
          headers: {
            "Content-type": "application/json",
          },
          body: JSON.stringify(strapiData), // Adding the parameters to the body.
        }).then(async (res) => {
          console.log(await res.json()); // Outputs the result
        });
        
       // The Rest of the code
      };
```

3. Load up [localhost:3000](http://localhost:3000) in your favorite browser to test the request.
![A Sample Screenshot](https://paper-attachments.dropbox.com/s_2AB6CD6238DB99E473ED13F1FBA90C2266A685066D162C9FB97895FCD9D8F400_1652041091647_Next+login.jpg)

4. Fill in the Username and the Email then hit submit. Open up your in-browser console to confirm that it was successfully added.
![A Sample Screenshot](https://paper-attachments.dropbox.com/s_2AB6CD6238DB99E473ED13F1FBA90C2266A685066D162C9FB97895FCD9D8F400_1652041337075_console.jpg)

5. Click on **Content Manager** in your Strapi’s admin panel to check if it was added to Strapi.
![](https://paper-attachments.dropbox.com/s_2AB6CD6238DB99E473ED13F1FBA90C2266A685066D162C9FB97895FCD9D8F400_1652146043538_account+added.jpg)

## Authenticating the User

After setting up Strapi, the next step is to use the token stored in strapi to authenticate the user. To do this, a link containing the token will be sent to the user’s email.

1. Open up your *index.js* file in the *pages* folder and add the following code to send the link to the user’s email in the `handlesubmit` function.
```js
    const handlesubmit = (e) => {
        e.preventDefault();
        const id = Math.trunc(Math.random() * 1000000); //Generating a random number and taking the first 10 numbers.
        console.log(id);
        let account = { // JWT payload
          id,
        };
        const SECRET = "this is a secret";
        const token = jwt.sign(account, SECRET);
        let message = `http://localhost:3000/chat/${token}`; // Adding the token to the message sent to the user.
        let data = {
          email, // User's email
          message,
        };
        fetch("/api/mail", {
          method: "POST", // POST request to /api//mail
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify(data),
        }).then(async (res) => {
          if (res.status === 200) {
            console.log(await res.json());
          } else {
            console.log(await res.json());
          }
        });
        setEmail("");
        setUser("");
      };
```
2. Navigate to http://localhost:3000, fill in a valid email and check your inbox to see if the message was successfully sent.

#### Verifying the Token

Once the link has been sent to the user and the user clicks on it, we need to get the token and verify it. This will prevent the user from generating random strings and in turn, it increases security.

1. Create a folder in the *pages* folder called *chat* and create a file in the chat folder named *[token].js*
![A Sample Screenshot](https://paper-attachments.dropbox.com/s_2AB6CD6238DB99E473ED13F1FBA90C2266A685066D162C9FB97895FCD9D8F400_1652305834555_token+file+creation.jpg)

2. Add the following code to the *[token.js]* file to get the token from the URL and verify it using the *JWT* *SECRET*.
```js
    import { useRouter } from "next/router";
    import { useEffect, useState } from "react";
    import jwt from "jsonwebtoken";
    export default function Chat() {
      const router = useRouter();
      const SECRET = "this is a secret"; // JWT Secret
      const [done, setDone] = useState("");
      const token = router.query.token; // Getting the token from the URL
      useEffect(() => {
        if (!router.isReady) return console.log("Loading... Please wait"); // Checking if the token has been fetched from the URL.
        try {
          const data = jwt.verify(token, SECRET); // Verifying the token using the secret
          console.log(data); // Logging out the Payload.
          setDone("done"); // Granting access to the chat page
        } catch (error) {
          router.push("/"); // Redirecting the user to the home page if an error occured
        }
      }, [token]); // Listens for a change in token
      return (
        <div>
          {done !== "done" ? ( // Waiting for access to be granted
            <h1>Verifying token..... Please wait</h1>
          ) : (
            <h1> Group Chat</h1>
          )}
        </div>
      );
    }
```

3. After verifying the token and getting the payload, the next step is to check if the verified token is the same as the one in Strapi. To do this, the *id* stored as payload in the token will be used as the query to Strapi. 

* Add the following code to the *[token.js]* file to get the *id* stored in the token.
```js
    export default function Chat() {
      // Rest of the code
      useEffect(() => {
        //Rest of the code
        try {
          const payload = jwt.verify(token, SECRET); // Verifying the token using the secret
          async function fetchData() {
            const data = await fetch(
              `http://localhost:1337/api/accounts/${payload.id}`
            );
            const account = await data.json(); // Getting the user's data
            console.log(account);
          }
          fetchData();
          setDone("done"); // granting access to the chat page
        } catch (error) {
          router.push("/"); // redirecting the user to the home page if an error occured
        }
      }, [token]); // Listens for a change in token
      // Rest of the code
    }
```    

-  After getting the user’s data from strapi,  add the following code to compare the tokens.
    
```js
    // Rest of code
    
    useEffect(() => {
          // Rest of the code.
    
          async function fetchData() {
            const data = await fetch(
              `http://localhost:1337/api/accounts/${payload.id}`
            );
            const account = await data.json();
            if (token !== account.data.attributes.token) return router.push("/"); // Verifying if the user exist in Strapi
            return;
          }
          fetchData();
         // Rest of the code
      }, [token])
    
    // Rest of the code
```

## Sending Messages

Now that security has been added to the chat application, it’s time to set up our chat environment.

> This article is a modification of [a previous article](https://strapi.io/blog/how-to-build-a-real-time-chat-forum-using-strapi-socket-io-react-and-mongo-db). Refer to it for clarity on the various features.

[Clone, fork or download the starter-ui file for this chat application using this GitHub link](https://github.com/divofred/chat-next-ui-starter.git).

1. Run `npm install` to install all the dependencies for this chat application. Once the installation is complete, run `npm run dev` to start up the chat application.

> Please ensure that you add the right Gmail credentials to the **mail.js** file. Also, ensure your strapi server is running. To do this, open your terminal and run `npm run develop` in your strapi’s project folder. 

2. When the chat application is up and running, head on to http://localhost:3000, create a new account and check your inputted email for the verification link. Once you click on the link you will be brought to a clean User Interface shown below.
![](https://paper-attachments.dropbox.com/s_2AB6CD6238DB99E473ED13F1FBA90C2266A685066D162C9FB97895FCD9D8F400_1653178373128_chat-ui.jpg)

## Setting up Socket.io

After starting up the chat application, the next step is to add chat functionality to the application.

#### Backend
1. Create a new collection named **message** in *Content-Type Builder* and add 2 fields, a *user* field and a *message* field.
    
![A Sample Screenshot](https://paper-attachments.dropbox.com/s_2AB6CD6238DB99E473ED13F1FBA90C2266A685066D162C9FB97895FCD9D8F400_1653373729294_image.png)


2. Now, open up the *chatapp* folder and add the backend functionality for *socket.io* to the *index.js* file in the *src* folder.
```js
    "use strict";
    module.exports = {
      /**
       * An asynchronous register function that runs before
       * your application is initialized.
       *
       * This gives you an opportunity to extend code.
       */
      register({ strapi }) {},
      /**
       * An asynchronous bootstrap function that runs before
       * your application gets started.
       *
       * This gives you an opportunity to set up your data model,
       * run jobs, or perform some special logic.
       */
      bootstrap(/* { strapi } */) {
        //strapi.server.httpServer is the new update for Strapi V4
        var io = require("socket.io")(strapi.server.httpServer, {
          cors: { // cors setup
            origin: "http://localhost:3000",
            methods: ["GET", "POST"],
            allowedHeaders: ["my-custom-header"],
            credentials: true,
          },
        });
        io.on("connection", function (socket) { //Listening for a connection from the frontend
          socket.on("join", ({ username }) => { // Listening for a join connection
            console.log("user connected");
            console.log("username is ", username);
            if (username) {
              socket.join("group"); // Adding the user to the group
              socket.emit("welcome", { // Sending a welcome message to the User
                user: "bot",
                text: `${username}, Welcome to the group chat`,
                userData: username,
              });
            } else {
              console.log("An error occurred");
            }
          });
          socket.on("sendMessage", async (data) => { // Listening for a sendMessage connection
            let strapiData = { // Generating the message data to be stored in Strapi
              data: {
                user: data.user,
                message: data.message,
              },
            };
            var axios = require("axios");
            await axios
              .post("http://localhost:1337/api/messages", strapiData)//Storing the messages in Strapi
              .then((e) => {
                socket.broadcast.to("group").emit("message", {//Sending the message to the group
                  user: data.username,
                  text: data.message,
                });
              })
              .catch((e) => console.log("error", e.message));
          });
        });
      },
    };
```    
#### Frontend
1. Add the following code to the *index.js* file in *next-chat/components* to set up socket.io and send messages.
```js
    import React, { useEffect, useState } from "react";
    import { Input } from "antd";
    import "antd/dist/antd.css";
    import "font-awesome/css/font-awesome.min.css";
    import Header from "./Header";
    import Messages from "./Messages";
    import List from "./List";
    import socket from "socket.io-client";
    import {
      ChatContainer,
      StyledContainer,
      ChatBox,
      StyledButton,
      SendIcon,
    } from "../pages/chat/styles";
    function ChatRoom({ username, id }) {
      const [messages, setMessages] = useState([]);
      const [message, setMessage] = useState("");
      const [users, setUsers] = useState([]);
      const io = socket("http://localhost:1337");//Connecting to Socket.io backend
      let welcome;
      useEffect(() => {
        io.emit("join", { username }, (error) => { //Sending the username to the backend as the user connects.
          if (error) return alert(error);
        });
        io.on("welcome", async (data, error) => {//Getting the welcome message from the backend
          let welcomeMessage = {
            user: data.user,
            message: data.text,
          };
          welcome = welcomeMessage;
          setMessages([welcomeMessage]);//Storing the Welcome Message
          await fetch("http://localhost:1337/api/messages")//Fetching all messages from Strapi
            .then(async (res) => {
              const response = await res.json();
              let arr = [welcome];
              response.data.map((one, i) => {
                arr = [...arr, one.attributes];
                setMessages((msgs) => arr);// Storing all Messages in a state variable
              });
            })
            .catch((e) => console.log(e.message));
        });
        io.on("message", async (data, error) => {//Listening for a message connection
          await fetch("http://localhost:1337/api/messages")
            .then(async (res) => {
              const response = await res.json();
              let arr = [welcome];
              response.data.map((one, i) => {
                arr = [...arr, one.attributes];
                setMessages((msgs) => arr);
              });
            })
            .catch((e) => console.log(e.message));
        });
      }, [username]);
      const sendMessage = (message) => {
        if (message) {
          io.emit("sendMessage", { message, user: username }, (error) => {// Sending the message to the backend
            if (error) {
              alert(error);
            }
          });
          setMessage("");
        } else {
          alert("Message can't be empty");
        }
      };
      const handleChange = (e) => {
        setMessage(e.target.value);
      };
      const handleClick = () => {
        sendMessage(message);
      };
      return (
        <ChatContainer>
          <Header room="Group Chat" />
          <StyledContainer>
            <List users={users} id={id} usersname={username}>
            <ChatBox>
              <Messages messages={messages} username={username} />
              <Input
                type="text"
                placeholder="Type your message"
                value={message}
                onChange={handleChange}
              />
              <StyledButton onClick={handleClick}>
                <SendIcon>
                  <i className="fa fa-paper-plane" />
                </SendIcon>
              </StyledButton>
            </ChatBox>
          </StyledContainer>
        </ChatContainer>
      );
    }
    export default ChatRoom;
```

2. Add the following code to the *index.js* file in *next-chat/components/Messages.* This code will loop through the messages and display them on the screen.
```js
    import React, { useEffect, useRef } from "react";
    import Message from "./Message/";
    import styled from "styled-components";
    function Messages(props) {
      const { messages, username: user } = props;
      const messagesEndRef = useRef(null);
      const scrollToBottom = () => {
        messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });//Scroll to bottom functionality.
      };
      useEffect(() => {
        scrollToBottom();
      }, [messages]);
      return (
        <StyledMessages>
          {messages.map((message, i) => (
              <div key={i} ref={messagesEndRef}>
                <Message message={message} username={user} />
              </div>
          ))}
        </StyledMessages>
      );
    }
    export default Messages;
    const StyledMessages = styled.div`
      padding: 5% 0;
      overflow: auto;
      flex: auto;
    `;
```    
3. Lastly, add the following code to the *index.js* file in *next-chat/components/Messages/Message.*  This code will neatly format the messages and position the messages in their rightful place.
    import React from "react";
    import { MessagesContainer, MessageBox, MessageText, SentBy } from "./styles";
    function Message(props) {
      const {
        username,
        message: { user, message },
      } = props;
      let sentByCurrentUser = false;
      if (user === username) {
        sentByCurrentUser = true;
      }
      const background = sentByCurrentUser ? "blue" : "dark";
      const textPosition = sentByCurrentUser ? "end" : "start";
      const textColor = sentByCurrentUser ? "white" : "dark";
      const sentBy = sentByCurrentUser ? "right" : "left";
      return (
        <MessagesContainer textPosition={textPosition}>
          <MessageBox background={background}>
            <MessageText color={textColor}>{message}</MessageText>
          </MessageBox>
          <SentBy sentBy={sentBy}>{user}</SentBy>
        </MessagesContainer>
      );
    }
    export default Message;
    
## Role-Based Auth

The previous section covered how to set up [socket.io](https://socket.io) for both the client and server sides of the application. This section will cover how to implement role-based auth in the chat application.

1. Create an *active user* collection type in **Strapi,** add a **users** field and socketid field, make sure it is set to a unique field and configure the roles permission.
![A Sample Screenshot](https://paper-attachments.dropbox.com/s_2AB6CD6238DB99E473ED13F1FBA90C2266A685066D162C9FB97895FCD9D8F400_1653403534109_image.png)

2. Add the following code to the index.js file in *chat-app/src* to make a POST and request to the *active users* collection type.
```js
    // The rest of Code
    socket.on("join", ({ username }) => {
            console.log("user connected");
            console.log("username is ", username);
            if (username) {
              socket.join("group");
              socket.emit("welcome", {
                user: "bot",
                text: `${username}, Welcome to the group chat`,
                userData: username,
              });
              let strapiData = {
              data: {
                users: username,
              },
            }
             await axios
                .post("http://localhost:1337/api/active-users", strapiData)//Storing the active users
                .then(async (e) => {
                  socket.emit("roomData", { done: "true" });
                })
              .catch((e) => {
                  if (e.message == "Request failed with status code 400") {//Checking if user exists
                    socket.emit("roomData", { done: "existing" });
                  }
                })
              
            } else {
              console.log("e no work");
            }
          });
    //The rest of the code
```

3. After storing and sending the user from the server, add the following code to the *index.js* file in *next-chat/components* to receive the data from the server.
```js
    //The rest of the code
     useEffect(() => {
        //The rest of the code
    
        io.on("roomData", async (data) => {
          await fetch("http://localhost:1337/api/active-users").then(async (e) => {
            setUsers(await e.json());//Fetching and storing the users in the users state variable
          });
       //The rest of the code
      }, [username]);
    
    //The rest of the code
```

4. Add the code below to the *index.js* file in *next-chat/components/List* get the users from the props passed to the List component and display them.
```js
    import React from "react";
    import styled from "styled-components";
    import { List as AntdList, Avatar } from "antd";
    function List(props) {
      const users = props.users.data;//Getting the users
      return (
        <StyledList>
          <ListHeading>Active Users</ListHeading>
          <AntdList
            itemLayout="horizontal"
            dataSource={users}
            renderItem={(user) => (
              <AntdList.Item>
                <AntdList.Item.Meta
                  avatar={
                    <Avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />
                  }
                  title={user.attributes.users}//Displaying the users
                />
                <button
                  style={
                    user.attributes.users === "Admin" || props.username !== "Admin"
                      ? { display: "none" }
                      : null
                  }//Verifying that the user is an Admin
                >
                  Delete
                </button>
              </AntdList.Item>
            )}
          />
        </StyledList>
      );
    }
    export default List;
    const StyledList = styled(AntdList)`
      margin-right: 10px;
      flex: 0 0 35%;
      padding: 20px;
      .ant-list-item-meta-content {
        flex-grow: 0;
      }
      h4 {
        font-size: 25px;
      }
      a {
        color: #097ef0;
      }
    `;
    const ListHeading = styled.div`
      color: #757591;
      font-size: 20px;
      font-style: oblique;
      border-bottom: 1px solid #757591;
    `;
```    
5. Populate the *Account* collection type with Admin as username. Head on to http://localhost:3000 to do so.
    
![A Sample Screenshot](https://paper-attachments.dropbox.com/s_2AB6CD6238DB99E473ED13F1FBA90C2266A685066D162C9FB97895FCD9D8F400_1653385657182_image.png)

6. Click on *Content-Type Builder* in your Strapi’s admin dashboard and edit the *username* filed to take in unique sets of usernames for the *Account* collection type.
    
![A Sample Screenshot](https://paper-attachments.dropbox.com/s_2AB6CD6238DB99E473ED13F1FBA90C2266A685066D162C9FB97895FCD9D8F400_1653385812507_image.png)

7. Hit finish and click on save.

## Adding Delete functionality.

This delete function is a feature made accessible to only the Admin user.

##### Points to Note

- Every user will always have to reload the page to view active users.
- When the Admin deletes a user, the page has to be refreshed to view changes.
- The deleted user will be redirected to the home page

**It’s time to configure the delete function.**

1. Open up the *index.js* file in *chatapp/src* and add the code below to set up the backend.
```js
    //The rest of the code
    bootstrap(/* { strapi } */) {
        //The rest of the code
      io.on("connection", function (socket) {
          // The rest of the code
          socket.on("kick", (data) => {//Listening for a kick event
            io.sockets.sockets.forEach((socket) => {
              if (socket.id === data.socketid) {
                socket.disconnect();//Disconnecting the User
                socket.removeAllListeners();
                return console.log("kicked", socket.id, data.socketid);
              } else {
                console.log("Couldn't kick", socket.id, data.socketid);
              }
            });
          });
        });
      },
    };
```

2. After setting up the backend, open up your *index.js* file in *next-chat/components/List* and add the following code. The code below will get the *socket id*, emit it to the server and delete the user from the database.
```js
    import React from "react";
    import styled from "styled-components";
    import { List as AntdList, Avatar } from "antd";
    import socket from "socket.io-client";
    function List(props) {
      const users = props.users.data;
      const handleClick = async (id, socketid) => {
        const io = socket("http://localhost:1337");
        await fetch("http://localhost:1337/api/active-users/" + id, {//Gets the id and delete the user from the active users collection.
          method: "Delete",
          headers: {
            "Content-type": "application/json",
          },
        })
          .then(async (e) => {
            io.emit("kick", { socketid }, (error) => {//When the
              if (error) return alert(error);
            });
            setTimeout(() => location.reload(), 3000);//Refreshes the page
          })
          .catch((e) => location.reload());//Refreshes the page if an error occured
      };
      return (
        <StyledList>
          <ListHeading>Active Users</ListHeading>
          <AntdList
            itemLayout="horizontal"
            dataSource={users}
            renderItem={(user) => (
              <AntdList.Item>
                <AntdList.Item.Meta
                  avatar={
                    <Avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />
                  }
                  title={user.attributes.users}
                />
                <button
                  style={
                    user.attributes.users === "Admin" || props.username !== "Admin"
                      ? { display: "none" }
                      : null
                  }
                  onClick={() => handleClick(user.id, user.attributes.socketid)}//Passing the socketid as parameter to the handleClick function
                >
                  Delete
                </button>
              </AntdList.Item>
            )}
          />
        </StyledList>
      );
    }
    export default List;
    //The rest of the code
```

## Conclusion

This article primarily aimed at teaching you how to set up different authenticating techniques in your application as well as enlightening you on how to utilize Strapi version 4.

Feel free to clone or fork the final code from the [GitHub repo](https://github.com/divofred/strapi-chat-app). As a next step, try to use this knowledge gained to build more complex applications using Strapi.