Build a real-time chat application with Nestjs and PostgreSQL
Tags: postgres, tutorial, programming, productivity
Published: 2022-05-12 20:22:14

The code for this tutorial is available on my [Github repository](https://github.com/icode247/Chat-with-Nestjs). Feel free to clone it as you follow the steps. Let's begin!

## What is NestJS?

[NestJS](https://nestjs.com/) is a Node.js framework for creating fast, testable, scalable, loosely coupled server-side applications that use TypeScript. It takes advantage of powerful HTTP server frameworks such as Express or Fastify. Nest adds a layer of abstraction to Node.js frameworks and exposes their APIs to developers. It supports database management systems like PostgreSQL and MySQL. NestJS also offers dependency injections Websockets and APIGetaways.

## What is a Websocket?

A WebSocket is a computer communications protocol that provides full-duplex communication channels over a single TCP connection. The IETF standardized the WebSocket protocol as RFC 6455 in 2011. The current specification is known as the HTML Living Standard. Unlike HTTP/HTTPS, Websocket are stateful protocols, which means the connection established between the server and the client will be alive unless terminated by the server or client; once a WebSocket connection is closed by one end, it extends to the other end.

## Prerequisites

This tutorial is a hands-on demonstration. To follow along, ensure you have installed the following:

- [Arctype](https://arctype.com/)
- [NodeJS](https://nodejs.org/)
- [PostgreSQL](https://www.postgresql.org/download/)

## Project setup

Before diving into coding, let's set up our NestJS project and our project structure. We'll start by creating the project folder. Then, open your terminal and run the following command:

```
mkdir chatapp && cd chatapp
```

## Creating the project folder

Then install the NestJS CLI with the command below:

```
npm i -g @nestjs/cli
```

When the installation is complete, run the command below to scaffold a NestJS project.

```
nest new chat
```

Choose your preferred npm package manager. For this tutorial, we'll use **npm** and wait for the necessary packages to be installed. Once the installation is completed, install **WebSocket** and **Socket.io** with the command below:

```
npm i --save @nestjs/websockets @nestjs/platform-socket.io
```    

Then, create a gateway application with the command below:

```
nest g gateway app
```

Now let's start our server by running the command below:

```
npm run start:dev
```    

## Setting up a Postgres database

We can now set up our Postgres database to store our user records with our server setup. First, we'll use [TypeORM](https://www.sqlalchemy.org/) (Object Relational Mapper) to connect our database with our application. To begin, we'll need to create a database with the following steps. First, switch to the system's Postgres user account.

```
sudo su - postgres
```

Then, create a new user account with the command below.

```
createuser --interactive
```

Next, create a new database. You can do that with the following command:

```
createdb chat
```

Now, we'll connect to the database we just created. First, open the app.module.ts file, and add the following code snippet below in the array of **imports[]**:

```
...
import { TypeOrmModule } from '@nestjs/typeorm';
import { Chat } from './chat.entity';
imports: [
   TypeOrmModule.forRoot({
     type: 'postgres',
     host: 'localhost',
     username: '<USERNAME>',
     password: '<PASSWORD>',
     database: 'chat',
     entities: [Chat],
     synchronize: true,
   }),
   TypeOrmModule.forFeature([Chat]),
 ],
...
```

In the above code snippet, we connected our application to a PostgresSQL database using the **TypeOrmModule forRoot** method and passed in our database credentials. Replace <USERNAME> and <PASSWORD> with the user and password you created for the **chat** database.

## Creating our chat Entity

Now that we've connected the application to your database create a chat entity to save the user's messages. To do that, create a **chat.entity.ts** file in the src folder and add the code snippet below:

```
import {
 Entity,
 Column,
 PrimaryGeneratedColumn,
 CreateDateColumn,
} from 'typeorm';
 
@Entity()
export class Chat {
 @PrimaryGeneratedColumn('uuid')
 id: number;
 
 @Column()
 email: string;
 
 @Column({ unique: true })
 text: string;
 
 @CreateDateColumn()
 createdAt: Date;
}
```

In the above code snippet, we created the columns for our chats using the **Entity**, **Column**, **CreatedDateColumn**, and **PrimaryGenerateColumn** decorators provided by TypeOrm.

## Setting up a web socket

Let's set up a Web socket connection in our server to send real-time messages. First, we'll import the required module we need with a code snippet below.

```
import {
 SubscribeMessage,
 WebSocketGateway,
 OnGatewayInit,
 WebSocketServer,
 OnGatewayConnection,
 OnGatewayDisconnect,
} from '@nestjs/websockets';
import { Socket, Server } from 'socket.io';
import { AppService } from './app.service';
import { Chat } from './chat.entity';
```    

In the above code snippet, we imported **SubscribeMessage()** to listen to events from the client, **WebSocketGateway()**, which will give access to socket.io; we also imported the **OnGatewayInit**, **OnGatewayConnection**, and **OnGatewayDisconnect** instances. This WebSocket instance enables you to know the state of your application. For example, we can have our server do stuff when a server joins or disconnects from the chat. Then we imported the **Chat** entity and the **AppService** which exposes the methods we need to save our user's messages.

```
@WebSocketGateway({
 cors: {
   origin: '*',
 },
})
```

To enable our client to communicate with the server, we enable CORS by in initializing the **WebSocketGateway.**

```
export class AppGateway
 implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect
{
 constructor(private appService: AppService) {}
 
 @WebSocketServer() server: Server;
 
 @SubscribeMessage('sendMessage')
 async handleSendMessage(client: Socket, payload: Chat): Promise<void> {
   await this.appService.createMessage(payload);
   this.server.emit('recMessage', payload);
 }
 
 afterInit(server: Server) {
   console.log(server);
   //Do stuffs
 }
 
 handleDisconnect(client: Socket) {
   console.log(`Disconnected: ${client.id}`);
   //Do stuffs
 }
 
 handleConnection(client: Socket, ...args: any[]) {
   console.log(`Connected ${client.id}`);
   //Do stuffs
 }
}
``` 

Next, in our **AppGateWay** class, we implemented the WebSocket instances we imported above. We created a constructor method and bind our AppService to have access to its methods. We created a server instance from the **WebSocketServer** decorators.

Then we create a **handleSendMessage** using the ***@SubscribeMessage()*** instance and a handleMessage() method to send data to our client-side.

When a message is sent to this function from the client, we save it in our database and emit the message back to all the connected users on our client side. We also have many other methods you can experiment with, like **afterInit,** which gets triggered after a client has connected, **handleDisconnect,** which gets triggered when a user disconnects. The **handleConnection** method starts when a user joins the connection.

## Creating a controller/service

Now let’s create our service and controller to save the chat and render our static page. Open the the **app.service.ts** file and update the content with the code snippet below:

```
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Chat } from './chat.entity';
 
@Injectable()
export class AppService {
 constructor(
   @InjectRepository(Chat) private chatRepository: Repository<Chat>,
 ) {}
 async createMessage(chat: Chat): Promise<Chat> {
   return await this.chatRepository.save(chat);
 }
 
 async getMessages(): Promise<Chat[]> {
   return await this.chatRepository.find();
 }
}
```    

Then update the **app.controller.ts** file with the code snippet below:

```
import { Controller, Render, Get, Res } from '@nestjs/common';
import { AppService } from './app.service';
import { Chat } from './chat.entity';
 
@Controller()
export class AppController {
 constructor(private readonly appService: AppService) {}
 
 @Get('/chat')
 @Render('index')
 Home() {
   return;
 }
 
 @Get('/api/chat')
 async Chat(@Res() res) {
   const messages = await this.appService.getMessages();
   res.json(messages);
 }
}
```

In the above code snippet, we created two routes to render our static page and the user’s messages.

## Serving our static page

Now let’s configure the application to render the static file and our pages. To do that, we'll implement server-side rendering. First, in your **main.ts** file, configure the application to static server files with the command below:

```
async function bootstrap() {
 ...
 app.useStaticAssets(join(__dirname, '..', 'static'));
 app.setBaseViewsDir(join(__dirname, '..', 'views'));
 app.setViewEngine('ejs');
 ...
}
```    

Next, create a **static** and a **views** folder in your **src** directory. In the views folder, create an **index.ejs** file and add the code snippet below:

```
<!DOCTYPE html>
<html lang="en">
 
<head>
 <!-- Required meta tags -->
 <meta charset="utf-8" />
 <meta name="viewport" content="width=device-width, initial-scale=1" />
 
 <!-- Bootstrap CSS -->
 <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
   integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous" />
 
 <title>Let Chat</title>
</head>
 
<body>
 <nav class="navbar navbar-light bg-light">
   <div class="container-fluid">
     <a class="navbar-brand">Lets Chat</a>
   </div>
 </nav>
 <div class="container">
   <div class="mb-3 mt-3">
     <ul style="list-style: none" id="data-container"></ul>
   </div>
   <div class="mb-3 mt-4">
     <input class="form-control" id="email" rows="3" placeholder="Your Email" />
   </div>
   <div class="mb-3 mt-4">
     <input class="form-control" id="exampleFormControlTextarea1" rows="3" placeholder="Say something..." />
   </div>
 </div>
 <script src="https://cdn.socket.io/4.3.2/socket.io.min.js"
   integrity="sha384-KAZ4DtjNhLChOB/hxXuKqhMLYvx3b5MlT55xPEiNmREKRzeEm+RVPlTnAn0ajQNs"
   crossorigin="anonymous"></script>
 <script src="app.js"></script>
 <!-- Option 1: Bootstrap Bundle with Popper -->
 <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
   integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
   crossorigin="anonymous"></script>
</body>
</html>
```

To speed things up in our templates, we used Bootstrap to add some stylings. Then we added two input fields and an unordered list to display the user's messages. We also included our **app.js** file which we will be creating later in this section and a link to the **socket.io** client.

Now create an **app.js** file and add the code snippet below:

```
const socket = io('http://localhost:3002');
const msgBox = document.getElementById('exampleFormControlTextarea1');
const msgCont = document.getElementById('data-container');
const email = document.getElementById('email');
 
//get old messages from the server
const messages = [];
function getMessages() {
 fetch('http://localhost:3002/api/chat')
   .then((response) => response.json())
   .then((data) => {
     loadDate(data);
     data.forEach((el) => {
       messages.push(el);
     });
   })
   .catch((err) => console.error(err));
}
getMessages();
 
//When a user press the enter key,send message.
msgBox.addEventListener('keydown', (e) => {
 if (e.keyCode === 13) {
   sendMessage({ email: email.value, text: e.target.value });
   e.target.value = '';
 }
});
 
//Display messages to the users
function loadDate(data) {
 let messages = '';
 data.map((message) => {
   messages += ` <li class="bg-primary p-2 rounded mb-2 text-light">
      <span class="fw-bolder">${message.email}</span>
      ${message.text}
    </li>`;
 });
 msgCont.innerHTML = messages;
}
 
//socket.io
//emit sendMessage event to send message
function sendMessage(message) {
 socket.emit('sendMessage', message);
}
//Listen to recMessage event to get the messages sent by users
socket.on('recMessage', (message) => {
 messages.push(message);
 loadDate(messages);
})
```

In the above code snippet, We created a socket.io instance and listened to the events on our server to send and receive a message from the server. We want the old chats to be available when a user joins the chat by default. Our application should look like the screenshot below:

![](https://lh5.googleusercontent.com/tOvgs2M2PsR-RfCrweNljlbseQjdFN2OzlxFAd6e1Ex03wlMvZMXoOnMwW4YzytxfE6vfON8l9WAPbYD6_BKZaDFmiiIohf4JqrXZthpyRsV8Bj2ohP8QCoPn61cMJxgdSq0eHp0bfeU-WjDXw)

## Viewing user data with Arctype

We have now successfully created our chat application. First, let's look at the users' data with Arctype. To begin, launch Arctype, click the MySQL tab, and enter the following MySQL credentials, as shown in the screenshot below:

![](https://lh4.googleusercontent.com/WeC9Nqv7E8CpYbVmFdemyk8APjLdbBlNZHkh3Dn5yFgLEFImTx_1yLBi-WsdVcJni46aJktxLssQllZysT1jFYGZH_iiD8l8r73NerJrlxEt1_Ab0anp3N-64ioRBrG1wZgXtCZ0Kd3fHEDbow)

Then, click on the chattable to show the user’s chat messages, as shown in the screenshot below:

![](https://lh6.googleusercontent.com/p9TVbZGMoKiTbRGvyVwFoE9eMgeDrfpRTRyeVpn-aNNzvcVsiqcMvVrbx_4HyYRp5LR--eRaalYvxqpI9ZvKes-m5lo_gkgth67uUkEF55V7RIT62N7bwYPDn36OY_irDtzQF5W9_etWV2xbpg)

## Testing the application

Now open the application in two different tabs or windows and try sending a message with a different email address as shown in the screenshot below:

![](https://lh5.googleusercontent.com/e6c8Gw6Lq_TFqk4-EFS63msyW2aeIUp5ywfBqwliWqseK2G1rbJDavPbrtHH9KxWKZhWP0V_zFkUkNyXoYQQ_QsFtv8i7HBKTnBc5575rmMcaANF5oXZEFP7V8GNlJGdR1Bjd_hj96mpTLcZqQ)

Also, when you look at your console, you’d see logs when a user joins and disconnects from the server which is handled by the **handleDisconnect** and **handleConnection** methods.

## Conclusion

Throughout this tutorial, we have explored how to create a real-time chat application with Nestjs and PostgreSQL. We started with a brief intro to Nestjs and WebSockets. Then we created a demo application to demonstrate the implementation. Hope you got the knowledge you seek. Perhaps you can learn more about WebSocket implementation from the Nestjs [documentation ](https://docs.nestjs.com/websockets/gateways)and add extend the application.++