author-mobile

By Nicholas Rowe,

August 28, 2024

Implementing WebSockets with NestJS for Real-Time Communication

Implementing WebSockets is essential for real-time communication in modern web applications. At Saigon Digital, we recently undertook a project focused on implementing WebSockets using NestJS to enable instantaneous communication between clients. This article delves into our approach, challenges, and innovative solutions we employed to implement WebSockets effectively.

Problem Statement

Specific applications require real-time data updates, such as push notifications, live data streaming, or critical operations like financial transactions and reservations. These scenarios demand timely and reliable data dissemination to ensure a seamless user experience and operational efficiency. This is where implementing WebSockets becomes essential.

Solution Implementation

For this project, we chose NestJS, a progressive Node.js framework known for its robust support for building scalable server-side applications, including real-time capabilities through WebSockets.

  • Choosing NestJS for Real-Time Capabilities: We selected NestJS because it has built-in support for WebSockets, making it ideal for handling real-time communication requirements.
  • Architecture Design: We designed a modular architecture using NestJS modules and services to manage WebSocket connections and handle incoming events.
  • Integration with Redis (optional): To manage WebSocket connections across multiple server instances, leverage Redis as a scalable pub/sub messaging broker, providing robust real-time communication support. Redis is a central hub for distributing messages and managing states across distributed systems. While optional, integrating Redis offers several advantages:
    • Scalability and Load Balancing: Redis facilitates horizontal scaling by allowing multiple server instances to share state seamlessly. This ensures that WebSocket connections are evenly distributed and managed across servers, enhancing overall system scalability.
    • Pub/Sub Messaging: Redis’ pub/sub model enables efficient broadcasting of messages to multiple subscribers (client connections). This is crucial for real-time applications that require instant updates and notifications.
    • Presence Management: Redis can store metadata about active WebSocket connections, such as user sessions or online status, providing a centralized mechanism for tracking and managing clients across distributed environments.
    • High Performance: Redis operates primarily in memory, making it exceptionally fast for read and write operations, crucial for handling large volumes of real-time data, and ensuring minimal latency in WebSocket communication.
    • Fault Tolerance and Resilience: Redis supports replication and persistence options, enhancing fault tolerance and ensuring data durability. This resilience is critical to maintaining the availability and reliability of WebSocket services.

Alternatives to Redis for WebSocket Management

If not using Redis, consider alternative approaches to managing WebSocket connections:

  • Local Memory Cache: For simpler setups, utilize in-memory caching solutions like memory-cache within NodeJS/NestJS, though these have limitations on scalability and distribution across server instances.
  • Database Solutions: Store WebSocket connection metadata in databases like MongoDB or MySQL, trading off some performance for more robust storage and querying capabilities.
  • Custom Middleware: Develop custom middleware or services tailored to your application’s needs, providing flexibility but requiring additional development effort and maintenance.

Why Choose NestJS with Socket?

NestJS provides several advantages for implementing WebSockets in real-time applications:

Scalability and Performance

NestJS leverages its underlying Node.js platform to handle concurrent connections efficiently, making it suitable for scalable applications with high traffic demands. NestJs also supports necessary packages, like: 

@nestjs/websockets @nestjs/platform-socket.io

Integration and Modularity

NestJS seamlessly integrates with other NestJS modules and third-party libraries, facilitating rapid development and modular code organization.

TypeScript Support

Built on TypeScript, NestJS offers strong typing and compile-time checks, reducing runtime errors and enhancing code maintainability.

Practice Applying Code to Implement WebSocket in NestJS

Let’s delve into the code implementation using NestJS to set up WebSocket communication.

1. Install Dependencies

First, ensure you have NestJS and necessary WebSocket libraries installed:

“npm install @nestjs/websockets socket.io @nestjs/platform-socket.io”

2. Create WebSocket Gateway

In NestJS, WebSocket functionality is encapsulated in a WebSocket Gateway. Create a new gateway using the Nest CLI:

“nest generate gateway socket”

This generates a new socket.gateway.ts file. Here’s an example of a basic WebSocket gateway setup:

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

@WebSocketGateway()
export class SocketGateway implements OnGatewayConnection, OnGatewayDisconnect {
    @WebSocketServer() server: Server;

    handleConnection(client: Socket) {
        console.log(`Client connected: ${client.id}`);
    }

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

    @SubscribeMessage('events')
    handleEvent(client: Socket, data: any): void {
        this.server.emit('events', data);
    }
}

We can also implement onGatewayInit to use the afterInit method. During initialization in the ‘afterInit()’ method, we introduced a simple log message confirming the WebSocket gateway’s setup. This provides valuable insights into the application’s lifecycle.

For details, the ‘handleConnection(client: any, …args: any[])’ method now manages new WebSocket connections. It logs client IDs and tracks the total number of active connections, ensuring comprehensive oversight.

For disconnection events, the ‘handleDisconnect(client: any)’ method handles logging and necessary cleanup operations when WebSocket clients disconnects.

We’ve also implemented a ‘@SubscribeMessage(‘ping’)’ method named ‘handleMessage(client: any, data: any)’. This function processes incoming ‘ping’ messages, logging their payloads and responding with a ‘pong’ event containing intentionally incorrect data. This deliberate mistake demonstrates our proactive handling and testing for potential errors.

We can get an authentication handshake like this:

const authHeader = socket.handshake.headers.authorization;

3. Integration with NestJS Modules

Integrate the WebSocket gateway into your NestJS application by adding it to the module:

import { Module } from '@nestjs/common';
import { SocketGateway } from './socket.gateway';

@Module({
    providers: [SocketGateway],
})
export class AppModule {}

4. Client-Side Implementation

On the client-side (e.g., frontend application), connect to the WebSocket server using a library like socket.io-client:

import { io } from 'socket.io-client';

const socket = io('ws://localhost:3000');

socket.on('connect', () => {
    console.log('Connected to WebSocket server');
});

socket.emit('events', { message: 'Hello, WebSocket!' });

4.1. Best practice

  • Enhanced Security: Implemented authentication and authorization mechanisms to secure WebSocket connections and prevent unauthorized access.
  • Real-Time Analytics: Integrated dashboards to monitor WebSocket traffic, track usage patterns, and optimise real-time performance.

4.2. Some advanced Features of NestJS WebSocket Integration

WebSocket in NestJS offers not only fundamental functionalities but also advanced and flexible features that enhance the efficiency of real-time application handling:

  1. Middleware and Interceptors: Like HTTP requests, NestJS allows middleware and interceptors to preprocess WebSocket events, from authentication to logging and data manipulation.
  2. Custom Gateways: WebSocket gateways can be created to manage connections and handle events tailored to specific application needs.
  3. Parameterization and Validation: NestJS supports parameterization and validation for data received via WebSocket, ensuring data integrity and accuracy.
  4. Clustering and Access Control: Utilize clustering and access control to manage and authorize WebSocket connections, restricting access to specific events and data.
  5. Event Broadcasting: NestJS facilitates event broadcasting to multiple subscribers across different channels, akin to Redis’ pub/sub model, making event propagation straightforward and efficient.

These features make NestJS a robust choice for developing real-time applications, catering to the complex and diverse requirements of modern software projects.

Conclusion

Implementing WebSockets with NestJS enables seamless real-time communication in web applications, effectively addressing critical needs like push notifications and live updates. By leveraging NestJS‘s robust features and optional integration with Redis, developers can build scalable, high-performance applications that meet modern real-time demands. Whether enhancing scalability, ensuring fault tolerance, or promoting code maintainability, NestJS with WebSockets is a powerful choice for developers aiming to deliver dynamic and responsive user experiences.

Ready to enhance your web applications with real-time communication? Contact Saigon Digital today to learn how our expertise in implementing WebSockets with NestJS can transform your project.

author-avatar
author-avatar

About the Author

Nicholas Rowe

As the CEO and Co-Founder of Saigon Digital, I bring a client-first approach to delivering high-level technical solutions that drive exceptional results to our clients across the world.

I’m interested in...

Give us some info about your project and we’ll be in touch

loading