WebSocket is a protocol that allows for real-time communication between web clients and servers. It enables bi-directional data transfer between the two, making it possible to build real-time applications that can send and receive data instantaneously. NestJS is a popular framework for building scalable, server-side applications with Node.js. In this article, we will explore how to use NestJS to build a WebSocket server and client, and create a real-time chat application.
What is NestJS?
NestJS is a framework for building scalable, server-side applications with Node.js. It is built on top of Express, which is a popular web framework for Node.js. NestJS provides a modular structure for building applications, with support for TypeScript and dependency injection. It also comes with built-in support for a range of features, including authentication, caching, and database integration.
What are WebSockets?
WebSocket is a protocol that allows for real-time communication between web clients and servers. Unlike traditional HTTP requests, which are one-way, WebSocket enables bi-directional data transfer between the two. This makes it possible to build real-time applications that can send and receive data instantaneously, without the need for constant polling or refreshing.
Setting up a NestJS Project
Before we can start building our WebSocket server and client, we need to set up a NestJS project. To do this, we can use the Nest CLI:
- Install the Nest CLI globally using npm:
- Create a new NestJS project:
npm install -g @nestjs/cli
nest new my-project
Installing Dependencies
Next, we need to install the dependencies required for our WebSocket server and client. We will be using the ws library for our WebSocket implementation, and the @nestjs/websockets package to integrate it with our NestJS application.
- Install the ws library:
- Install the @nestjs/websockets package:
npm install –save ws
npm install –save @nestjs/websockets
Building a WebSocket Server
Now that we have our NestJS project set up and our dependencies installed, we can start building our WebSocket server. To do this, we need to create a WebSocket gateway:
Creating a WebSocket Gateway
A WebSocket gateway is a class that handles WebSocket connections and events. We can create a WebSocket gateway using the @WebSocketGateway decorator and the WebSocketServer object provided by the @nestjs/websockets package:
@WebSocketGateway()export class ChatGateway {@WebSocketServer() server: Server;handleConnection(client: Socket) {// Handle new WebSocket connection}
handleDisconnect(client: Socket) {// Handle WebSocket disconnection}}
In the example above, we have created a ChatGateway class that uses the @WebSocketGateway decorator to indicate that it is a WebSocket gateway. We have also defined a handleConnection method that will be called when a new WebSocket connection is established, and a handleDisconnect method that will be called when a WebSocket connection is closed.
Note that we have also used the @WebSocketServer decorator to inject the WebSocket server instance into our gateway. This allows us to send messages to all connected clients using the server instance’s send method.
Handling WebSocket Events
Now that we have our WebSocket gateway set up, we can start handling WebSocket events. The two main events that we will be handling are message and error:
@WebSocketGateway()export class ChatGateway {@WebSocketServer() server: Server;handleConnection(client: Socket) {// Handle new WebSocket connection}
handleDisconnect(client: Socket) {// Handle WebSocket disconnection}
@SubscribeMessage('message')handleMessage(client: Socket, payload: any) {// Handle WebSocket message}
@SubscribeMessage('error')handleError(client: Socket, error: Error) {// Handle WebSocket error}}
In the example above, we have defined two new methods: handleMessage and handleError. These methods are decorated with the @SubscribeMessage decorator, which indicates that they should be called when a WebSocket message or error event is received.
The handleMessage method takes two arguments: the client that sent the message, and the message payload. The handleError method takes two arguments: the client that encountered the error, and the error object.
Sending Messages to Clients
Now that we can handle incoming WebSocket messages, we need to be able to send messages back to clients. We can do this using the server instance that we injected earlier:
@WebSocketGateway()export class ChatGateway {@WebSocketServer() server: Server;handleConnection(client: Socket) {// Handle new WebSocket connection}
handleDisconnect(client: Socket) {// Handle WebSocket disconnection}
@SubscribeMessage('message')handleMessage(client: Socket, payload: any) {// Handle WebSocket messagethis.server.emit('message', payload);}
@SubscribeMessage('error')handleError(client: Socket, error: Error) {// Handle WebSocket error}}
In the example above, we have modified the handleMessage method to send the received message back to all connected clients using the server instance’s emit method.
Building a WebSocket Client
Now that we have our WebSocket server set up, we can start building our WebSocket client. To do this, we will be using the ws library:
Connecting to the WebSocket Server
To connect to our WebSocket server, we need to create a new WebSocket instance:
const WebSocket = require('ws');const ws = new WebSocket('ws://localhost:3000');
In the example above, we have created a new WebSocket instance that connects to our server at ws://localhost:3000.
Sending Messages to the Server
Now that we have connected to our WebSocket server, we can start sending messages to it. We can do this using the send method provided by the WebSocket instance:
const WebSocket = require('ws');const ws = new WebSocket('ws://localhost:3000');
ws.on('open', function() {ws.send('Hello, server!');});
In the example above, we have added an event listener to our WebSocket instance that sends a ‘Hello, server!’ message to the server when the connection is opened.
Receiving Messages from the Server
Now that we can send messages to the server, we need to be able to receive messages from it. We can do this by adding an event listener to our WebSocket instance:
const WebSocket = require('ws');const ws = new WebSocket('ws://localhost:3000');
ws.on('open', function() {ws.send('Hello, server!');});
ws.on('message', function(data) {console.log('Received message:', data);});
In the example above, we have added an event listener to our WebSocket instance that logs any received messages to the console.
Building a Real-Time Chat Application
Now that we have our WebSocket server and client set up, we can start building a real-time chat application. To do this, we will use the ChatGateway that we created earlier:
Creating a Chat Service
Our chat application will need a service to handle messages and user data. We can create a new service using the Nest CLI:
- Create a new service:
nest generate service chat
Next, we need to add some methods to our ChatService to handle incoming messages and user data:
import { Injectable } from '@nestjs/common';import { Socket } from 'socket.io';@Injectable()export class ChatService {private users = new Map();
addUser(id: string, user: string) {this.users.set(id, user);}
removeUser(id: string) {this.users.delete(id);}
getUsers() {return Array.from(this.users.values());}
handleMessage(client: Socket, message: string) {const username = this.users.get(client.id);this.gateway.server.emit('message', { username, message });}}
In the example above, we have created a ChatService class that defines methods for adding and removing users, getting a list of connected users, and handling incoming messages.
Note that we have also injected our ChatGateway using the constructor, which allows us to access the WebSocket server instance and send messages back to clients.
Connecting to the Chat Service
Now that we have our ChatService set up, we can connect to it from our ChatGateway:
@WebSocketGateway()export class ChatGateway {@WebSocketServer() server: Server;constructor(private chatService: ChatService) {}
handleConnection(client: Socket) {const username = `User${Math.floor(Math.random() * 1000)}`;this.chatService.addUser(client.id, username);this.server.emit('users', this.chatService.getUsers());}
handleDisconnect(client: Socket) {this.chatService.removeUser(client.id);this.server.emit('users', this.chatService.getUsers());}
@SubscribeMessage('message')handleMessage(client: Socket, message: any) {this.chatService.handleMessage(client, message);}
@SubscribeMessage('username')handleUsername(client: Socket, username: string) {this.chatService.addUser(client.id, username);this.server.emit('users', this.chatService.getUsers());}}
In the example above, we have modified our ChatGateway to use our ChatService to handle user data and incoming messages. We have also added a handleUsername method that allows clients to set their username when they connect to the server.
Note that we have injected our ChatService into our ChatGateway using the constructor, which allows us to access its methods and properties.
Building the Chat Client
Now that we have our WebSocket server and ChatService set up, we can start building the Chat client. We will be using the ws library and React.js for this:
- Create a new React app:
- Install the ws library:
- Install the @nestjs/websockets package:
npx create-react-app my-chat-app
npm install –save ws
npm install –save @nestjs/websockets
Next, we need to add some components to our React app to handle user input and display messages:
import React, { useState, useEffect } from 'react';import logo from './logo.svg';import './App.css';import { w3cwebsocket as WebSocket } from 'websocket';const ws = new WebSocket('ws://localhost:3000');
function App() {const [username, setUsername] = useState('');const [message, setMessage] = useState('');const [messages, setMessages] = useState([]);const [users, setUsers] = useState([]);
useEffect(() => {ws.onopen = () => {console.log('Connected to server');};
ws.onmessage = (event) => {const data = JSON.parse(event.data);
if (data.type === 'message') {setMessages([...messages, data]);} else if (data.type === 'users') {setUsers(data.users);}};}, []);
function handleUsernameChange(event) {setUsername(event.target.value);}
function handleMessageChange(event) {setMessage(event.target.value);}
function handleSendMessage() {ws.send(JSON.stringify({ type: 'message', message }));setMessage('');}
function handleSetUsername() {ws.send(JSON.stringify({ type: 'username', username }));}
return (<div className="App"><header className="App-header"><img src={logo} className="App-logo" alt="logo" /><h1 className="App-title">Chat App</h1></header><div className="App-body"><div className="App-users"><h2>Users</h2><ul>{users.map((user) => (<li key={user}>{user}</li>))}</ul><input type="text" value={username} onChange={handleUsernameChange} /><button onClick={handleSetUsername}>Set Username</button></div><div className="App-chat"><h2>Chat</h2><ul>{messages.map((message) => (<li key={message.timestamp}><strong>{message.username}:</strong> {message.message}</li>))}</ul><input type="text" value={message} onChange={handleMessageChange} /><button onClick={handleSendMessage}>Send</button></div></div></div>);}
export default App;
In the example above, we have created a new React app that uses the WebSocket instance that we created earlier to connect to our WebSocket server. We have also added components for handling user input and displaying messages, as well as state variables for storing user data and message history.
Note that we are sending and receiving data in JSON format, and that we are using the useEffect hook to set up our WebSocket event listeners when the component is mounted.
FAQ
What is NestJS?
NestJS is a framework for building scalable, server-side applications with Node.js. It is built on top of Express, which is a popular web framework for Node.js. NestJS provides a modular structure for building applications, with support for TypeScript and dependency injection. It also comes with built-in support for a range of features, including authentication, caching, and database integration.
What are WebSockets?
WebSocket is a protocol that allows for real-time communication between web clients and servers. Unlike traditional HTTP requests, which are one-way, WebSocket enables bi-directional data transfer between the two. This makes it possible to build real-time applications that can send and receive data instantaneously, without the need for constant polling or refreshing.
How do I set up a NestJS project?
To set up a NestJS project, you can use the Nest CLI. First, install the Nest CLI globally using npm:
npm install -g @nestjs/cli
Next, create a new NestJS project:
nest new my-project
How do I install dependencies for a NestJS project?
To install dependencies for a NestJS project, you can use npm. For example, to install the ws library and the @nestjs/websockets package:
npm install --save ws @nestjs/websockets
How do I build a WebSocket server with NestJS?
To build a WebSocket server with NestJS, you can create a WebSocket gateway that handles WebSocket connections and events. You can use the @WebSocketGateway decorator and the