Build a Real-time Chat Application With Nestjs and PostgreSQL | by Arctype | May, 2022

On this tutorial, you’ll be taught so as to add real-time chatting options to your Nestjs net utility utilizing net sockets

Build a real-time chat application with Nestjs and PostgreSQL

The code for this tutorial is on the market on my Github repository. Be happy to clone it as you comply with the steps. Let’s start!

NestJS is a Node.js framework for creating quick, testable, scalable, loosely coupled server-side functions that use TypeScript. It takes benefit of highly effective HTTP server frameworks comparable to Categorical or Fastify.

Nest provides a layer of abstraction to Node.js frameworks and exposes their APIs to builders. It helps database administration techniques like PostgreSQL and MySQL. NestJS additionally affords dependency injections Websockets and APIGetaways.

A WebSocket is a pc communications protocol that gives full-duplex communication channels over a single TCP connection. The IETF standardized the WebSocket protocol as RFC 6455 in 2011.

The present specification is called the HTML Residing Customary. Not like HTTP/HTTPS, Websocket are stateful protocols, which suggests the connection established between the server and the shopper shall be alive until terminated by the server or shopper; as soon as a WebSocket connection is closed by one finish, it extends to the opposite finish.

Conditions

This tutorial is a hands-on demonstration. To comply with alongside, guarantee you might have put in the next:

Earlier than diving into coding, let’s arrange our NestJS challenge and our challenge construction. We’ll begin by creating the challenge folder. Then, open your terminal and run the next command:

mkdir chatapp && cd chatapp

Then set up the NestJS CLI with the command under:

npm i -g @nestjs/cli

When the set up is full, run the command under to scaffold a NestJS challenge.

nest new chat

Select your most well-liked npm bundle supervisor. For this tutorial, we’ll use npm and look ahead to the mandatory packages to be put in. As soon as the set up is accomplished, set up WebSocket and Socket.io with the command under:

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

Then, create a gateway utility with the command under:

nest g gateway app

Now let’s begin our server by operating the command under:

npm run begin:dev

We are able to now arrange our Postgres database to retailer our consumer information with our server setup.

First, we’ll use TypeORM (Object Relational Mapper) to attach our database with our utility. To start, we’ll must create a database with the next steps. First, change to the system’s Postgres consumer account.

sudo su - postgres

Then, create a brand new consumer account with the command under.

createuser --interactive

Subsequent, create a brand new database. You are able to do that with the next command:

createdb chat

Now, we’ll hook up with the database we simply created. First, open the app.module.ts file, and add the next code snippet under within 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]),
],
...

Within the above code snippet, we related our utility to a PostgresSQL database utilizing the TypeOrmModule forRoot technique and handed in our database credentials. Exchange <USERNAME> and <PASSWORD> with the consumer and password you created for the chat database.

Now that we’ve related the appliance to your database create a chat entity to save lots of the consumer’s messages. To do this, create a chat.entity.ts file within the src folder and add the code snippet under:

import 
Entity,
Column,
PrimaryGeneratedColumn,
CreateDateColumn,
from 'typeorm';

@Entity()
export class Chat
@PrimaryGeneratedColumn('uuid')
id: quantity;

@Column()
electronic mail: string;

@Column( distinctive: true )
textual content: string;

@CreateDateColumn()
createdAt: Date;

Within the above code snippet, we created the columns for our chats utilizing the Entity, Column, CreatedDateColumn, and PrimaryGenerateColumn decorators supplied by TypeOrm.

Let’s arrange a Internet socket connection in our server to ship real-time messages. First, we’ll import the required module we want with a code snippet under.

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';

Within the above code snippet, we imported SubscribeMessage() to take heed to occasions from the shopper, WebSocketGateway(), which is able to give entry to socket.io; we additionally imported the OnGatewayInit, OnGatewayConnection, and OnGatewayDisconnect situations.

This WebSocket occasion allows you to know the state of your utility. For instance, we will 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 strategies we have to save our consumer’s messages.

@WebSocketGateway(
cors:
origin: '*',
,
)

To allow our shopper to speak with the server, we allow CORS by in initializing the WebSocketGateway.

export class AppGateway
implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect

constructor(personal appService: AppService)

@WebSocketServer() server: Server;

@SubscribeMessage('sendMessage')
async handleSendMessage(shopper: Socket, payload: Chat): Promise<void>
await this.appService.createMessage(payload);
this.server.emit('recMessage', payload);

afterInit(server: Server)
console.log(server);
//Do stuffs

handleDisconnect(shopper: Socket)
console.log(`Disconnected: $shopper.id`);
//Do stuffs

handleConnection(shopper: Socket, ...args: any[])
console.log(`Linked $shopper.id`);
//Do stuffs

Subsequent, in our AppGateWay class, we carried out the WebSocket situations we imported above. We created a constructor technique and bind our AppService to have entry to its strategies. We created a server occasion from the WebSocketServer decorators.

Then we create a handleSendMessage utilizing the @SubscribeMessage() occasion and a handleMessage() technique to ship knowledge to our client-side.

When a message is shipped to this operate from the shopper, we reserve it in our database and emit the message again to all of the related customers on our shopper facet.

We even have many different strategies you may experiment with, like afterInit, which will get triggered after a shopper has related, handleDisconnect, which will get triggered when a consumer disconnects. The handleConnection technique begins when a consumer joins the connection.

Now let’s create our service and controller to save lots of the chat and render our static web page. Open the app.service.ts file and replace the content material with the code snippet under:

import  Injectable  from '@nestjs/widespread';
import InjectRepository from '@nestjs/typeorm';
import Repository from 'typeorm';
import Chat from './chat.entity';

@Injectable()
export class AppService
constructor(
@InjectRepository(Chat) personal chatRepository: Repository<Chat>,
)
async createMessage(chat: Chat): Promise<Chat>
return await this.chatRepository.save(chat);

async getMessages(): Promise<Chat[]>
return await this.chatRepository.discover();

Then replace the app.controller.ts file with the code snippet under:

import  Controller, Render, Get, Res  from '@nestjs/widespread';
import AppService from './app.service';
import Chat from './chat.entity';

@Controller()
export class AppController
constructor(personal readonly appService: AppService)

@Get('/chat')
@Render('index')
Residence()
return;

@Get('/api/chat')
async Chat(@Res() res)
const messages = await this.appService.getMessages();
res.json(messages);

Within the above code snippet, we created two routes to render our static web page and the consumer’s messages.

Now let’s configure the appliance to render the static file and our pages. To do this, we’ll implement server-side rendering. First, in your essential.ts file, configure the appliance to static server recordsdata with the command under:

async operate bootstrap() 
...
app.useStaticAssets(be a part of(__dirname, '..', 'static'));
app.setBaseViewsDir(be a part of(__dirname, '..', 'views'));
app.setViewEngine('ejs');
...

Subsequent, create a static and a views folder in your src listing. Within the views folder, create an index.ejs file and add the code snippet under:

<!DOCTYPE html>
<html lang="en">

<head>
<!-- Required meta tags -->
<meta charset="utf-8" />
<meta identify="viewport" content material="width=device-width, initial-scale=1" />

<!-- Bootstrap CSS -->
<hyperlink href="https://cdn.jsdelivr.web/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="nameless" />

<title>Let Chat</title>
</head>

<physique>
<nav class="navbar navbar-light bg-light">
<div class="container-fluid">
<a category="navbar-brand">Lets Chat</a>
</div>
</nav>
<div class="container">
<div class="mb-3 mt-3">
<ul type="list-style: none" id="data-container"></ul>
</div>
<div class="mb-3 mt-4">
<enter class="form-control" id="electronic mail" rows="3" placeholder="Your E mail" />
</div>
<div class="mb-3 mt-4">
<enter class="form-control" id="exampleFormControlTextarea1" rows="3" placeholder="Say one thing..." />
</div>
</div>
<script src="https://cdn.socket.io/4.3.2/socket.io.min.js"
integrity="sha384-KAZ4DtjNhLChOB/hxXuKqhMLYvx3b5MlT55xPEiNmREKRzeEm+RVPlTnAn0ajQNs"
crossorigin="nameless"></script>
<script src="app.js"></script>
<!-- Possibility 1: Bootstrap Bundle with Popper -->
<script src="https://cdn.jsdelivr.web/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
crossorigin="nameless"></script>
</physique>
</html>

To hurry issues up in our templates, we used Bootstrap so as to add some stylings. Then we added two enter fields and an unordered listing to show the consumer’s messages. We additionally included our app.js file which we shall be creating later on this part and a hyperlink to the socket.io shopper.

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

const socket = io('http://localhost:3002');
const msgBox = doc.getElementById('exampleFormControlTextarea1');
const msgCont = doc.getElementById('data-container');
const electronic mail = doc.getElementById('electronic mail');

//get outdated messages from the server
const messages = [];
operate getMessages()
fetch('http://localhost:3002/api/chat')
.then((response) => response.json())
.then((knowledge) =>
loadDate(knowledge);
knowledge.forEach((el) =>
messages.push(el);
);
)
.catch((err) => console.error(err));

getMessages();

//When a consumer press the enter key,ship message.
msgBox.addEventListener('keydown', (e) =>
if (e.keyCode === 13)
sendMessage( electronic mail: electronic mail.worth, textual content: e.goal.worth );
e.goal.worth = '';

);

//Show messages to the customers
operate loadDate(knowledge)
let messages = '';
knowledge.map((message) =>
messages += ` <li class="bg-primary p-2 rounded mb-2 text-light">
<span class="fw-bolder">$message.electronic mail</span>
$message.textual content
</li>`;
);
msgCont.innerHTML = messages;

//socket.io
//emit sendMessage occasion to ship message
operate sendMessage(message)
socket.emit('sendMessage', message);

//Take heed to recMessage occasion to get the messages despatched by customers
socket.on('recMessage', (message) =>
messages.push(message);
loadDate(messages);
)

Within the above code snippet, We created a socket.io occasion and listened to the occasions on our server to ship and obtain a message from the server. We wish the outdated chats to be accessible when a consumer joins the chat by default. Our utility ought to appear to be the screenshot under:

Build a real-time chat application with Nestjs and PostgreSQL

We now have now efficiently created our chat utility. First, let’s take a look at the customers’ knowledge with Arctype. To start, launch Arctype, click on the MySQL tab, and enter the next MySQL credentials, as proven within the screenshot under:

Build a real-time chat application with Nestjs and PostgreSQL

Then, click on on the chat desk to point out the consumer’s chat messages, as proven within the screenshot under:

Build a real-time chat application with Nestjs and PostgreSQL

Now open the appliance in two totally different tabs or home windows and check out sending a message with a distinct electronic mail tackle as proven within the screenshot under:

Build a real-time chat application with Nestjs and PostgreSQL

Additionally, if you take a look at your console, you’d see logs when a consumer joins and disconnects from the server which is dealt with by the handleDisconnect and handleConnection strategies.

All through this tutorial, we have now explored easy methods to create a real-time chat utility with Nestjs and PostgreSQL. We began with a short intro to Nestjs and WebSockets. Then we created a demo utility to exhibit the implementation. Hope you bought the data you search. Maybe you may be taught extra about WebSocket implementation from the Nestjs documentation and add prolong the appliance.

More Posts