DEV Community

Cover image for Real-time Applications with NestJS and WebSockets
Ezile Mdodana
Ezile Mdodana

Posted on

Real-time Applications with NestJS and WebSockets

Real-time applications have become an integral part of modern web development, enabling instant communication and dynamic interactions. NestJS, with its powerful framework and support for WebSockets, makes it easy to build such applications. In this article, we'll explore how to create real-time applications with NestJS and WebSockets, covering key concepts, implementation steps, and best practices.

Why Use WebSockets?

WebSockets provide a full-duplex communication channel over a single TCP connection, allowing real-time data exchange between the client and server. Unlike traditional HTTP requests, WebSockets maintain an open connection, enabling instant updates without the need for polling.

Setting Up a NestJS Project

First, create a new NestJS project:

nest new real-time-app
Enter fullscreen mode Exit fullscreen mode

Navigate to the project directory:

cd real-time-app
Enter fullscreen mode Exit fullscreen mode

Installing WebSocket Dependencies

NestJS provides a WebSocket module out of the box. Install the necessary package:

npm install @nestjs/websockets @nestjs/platform-socket.io
Enter fullscreen mode Exit fullscreen mode

Creating a WebSocket Gateway

A gateway in NestJS acts as a controller for WebSocket events. Create a new gateway using the NestJS CLI:

nest generate gateway chat
Enter fullscreen mode Exit fullscreen mode

This will create a new chat.gateway.ts file. Let's define our gateway to handle real-time chat messages:

import {
  SubscribeMessage,
  WebSocketGateway,
  OnGatewayInit,
  OnGatewayConnection,
  OnGatewayDisconnect,
  WebSocketServer,
} from '@nestjs/websockets';
import { Logger } from '@nestjs/common';
import { Socket, Server } from 'socket.io';

@WebSocketGateway()
export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect {
  @WebSocketServer() server: Server;
  private logger: Logger = new Logger('ChatGateway');

  @SubscribeMessage('message')
  handleMessage(client: Socket, payload: string): void {
    this.server.emit('message', payload);
  }

  afterInit(server: Server) {
    this.logger.log('Init');
  }

  handleConnection(client: Socket, ...args: any[]) {
    this.logger.log(`Client connected: ${client.id}`);
  }

  handleDisconnect(client: Socket) {
    this.logger.log(`Client disconnected: ${client.id}`);
  }
}

Enter fullscreen mode Exit fullscreen mode

Explanation

  • @WebSocketGateway(): Decorator to mark the class as a WebSocket gateway
  • @WebSocketServer(): Decorator to inject the WebSocket server instance
  • handleMessage(): Method to handle incoming messages and broadcast them to all connected clients
  • afterInit(): Lifecycle hook called after the gateway is initialized
  • handleConnection(): Lifecycle hook called when a client connects
  • handleDisconnect(): Lifecycle hook called when a client disconnects

Handling Events on the Client-Side

To handle WebSocket events on the client side, you can use the socket.io-client library. Install it using:

npm install socket.io-client
Enter fullscreen mode Exit fullscreen mode

Here’s an example of a simple client-side implementation in an HTML file:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Chat App</title>
  <script src="https://cdn.socket.io/4.0.1/socket.io.min.js"></script>
  <script>
    document.addEventListener('DOMContentLoaded', () => {
      const socket = io('http://localhost:3000');

      const form = document.getElementById('form');
      const input = document.getElementById('input');
      const messages = document.getElementById('messages');

      form.addEventListener('submit', (e) => {
        e.preventDefault();
        if (input.value) {
          socket.emit('message', input.value);
          input.value = '';
        }
      });

      socket.on('message', (message) => {
        const item = document.createElement('li');
        item.textContent = message;
        messages.appendChild(item);
      });
    });
  </script>
</head>
<body>
  <ul id="messages"></ul>
  <form id="form" action="">
    <input id="input" autocomplete="off" /><button>Send</button>
  </form>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Explanation

  • Socket.IO: The socket.io-client library is used to handle WebSocket connections
  • Connecting to the Server: io('http://localhost:3000') establishes a connection to the WebSocket server
  • Sending Messages: The form submission event sends messages to the server using socket.emit('message', input.value)
  • Receiving Messages: The socket.on('message') event handler updates the DOM with new messages

Best Practices

  1. Authentication and Authorization: Ensure that WebSocket connections are authenticated and authorized. Use middlewares or guards to secure your WebSocket endpoints

  2. Error Handling: Implement proper error handling for your WebSocket events to ensure robustness

  3. Scalability: Use tools like Redis for pub/sub patterns if you need to scale your WebSocket server

  4. Monitoring and Logging: Integrate monitoring tools to keep track of WebSocket connections and events

Conclusion

Building real-time applications with NestJS and WebSockets is straightforward and efficient. By leveraging NestJS's WebSocket module, you can create highly interactive and responsive applications. Follow best practices for authentication, error handling, and scalability to ensure your real-time application is robust and secure.

My way is not the only way!

Top comments (0)