Lesson 29-NestJS Integration with WebSockets Applications

Integration with WebSockets

Environment Preparation

  • Node.js: Install the latest stable version.
  • NestJS: Initialize a project using npm or yarn.

Creating a NestJS Project

npm init nest new websocket-app
cd websocket-app

Installing WebSocket Dependencies

npm install @nestjs/websockets

Configuring the WebSocket Module

Import the WebSocketModule in src/app.module.ts.

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { WebSocketModule } from '@nestjs/websockets';

@Module({
  imports: [WebSocketModule.register({ namespace: 'chat' })],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

Creating a WebSocket Service

Generate a new service to handle WebSocket connections.

nest generate websocket chat

Edit src/chat/chat.gateway.ts:

import { WebSocketGateway, SubscribeMessage, MessageBody, ConnectedSocket, OnGatewayConnection, OnGatewayDisconnect } from '@nestjs/websockets';
import { Socket } from 'socket.io-client';

@WebSocketGateway({ namespace: 'chat' })
export class ChatGateway implements OnGatewayConnection, OnGatewayDisconnect {
  constructor() {}

  handleConnection(@ConnectedSocket() client: Socket) {
    console.log('Client connected');
  }

  handleDisconnect(@ConnectedSocket() client: Socket) {
    console.log('Client disconnected');
  }

  @SubscribeMessage('message')
  handleMessage(@ConnectedSocket() client: Socket, @MessageBody() data: any): void {
    this.server.to(client.id).emit('message', data);
  }
}

Connection Testing

Start the server:

npm run start:dev

Test the connection using a client library like socket.io-client:

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

socket.on('connect', () => {
  console.log('Connected to server');
  socket.emit('message', { text: 'Hello from the client!' });
});

socket.on('message', (data) => {
  console.log('Received message:', data.text);
});

Gateways

Gateways are special classes in NestJS used to handle WebSocket connections. They enable bidirectional communication between the application and clients via the WebSocket protocol.

Definition:

  • Create a new gateway class that extends WebSocketGateway.
  • Use the @WebSocketGateway() decorator to configure the gateway.
import { WebSocketGateway, SubscribeMessage, MessageBody, ConnectedSocket } from '@nestjs/websockets';
import { Socket } from 'socket.io-client';

@WebSocketGateway({ namespace: 'chat' })
export class ChatGateway {
  @SubscribeMessage('message')
  handleMessage(@ConnectedSocket() client: Socket, @MessageBody() data: any): void {
    this.server.to(client.id).emit('message', data);
  }
}

Exception Filters

Exception filters are used to catch and handle exceptions in controllers. They can be registered globally or applied to specific controllers or routes.

Definition:

  • Create a class that implements the Catch decorator.
  • Implement the catch method to handle exceptions.
import { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus } from '@nestjs/common';

@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse();
    const status = exception.getStatus();

    response.status(status).json({
      statusCode: status,
      message: exception.message,
      error: 'Some error occurred',
    });
  }
}

Pipes

Pipes are used to validate and transform incoming data. They run before the route handler processes the request.

Definition:

  • Create a class that implements the PipeTransform interface.
  • Implement the transform method to process data.
import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';

@Injectable()
export class ValidationPipe implements PipeTransform<any> {
  transform(value: any, metadata: ArgumentMetadata) {
    if (!value || typeof value !== 'object') {
      throw new BadRequestException('Invalid data format');
    }
    return value;
  }
}

Guards

Guards protect routes or methods from unauthorized access. They run before the request reaches the controller.

Definition:

  • Create a class that implements the CanActivate interface.
  • Implement the canActivate method to determine if the request should proceed.
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(context: ExecutionContext) {
    // Get the request object
    const request = context.switchToHttp().getRequest();
    // Check if the user is authenticated
    return request.isAuthenticated();
  }
}

Interceptors

Interceptors modify method calls entering and leaving controllers. They can run before the request reaches the controller or after the response is sent to the client.

Definition:

  • Create a class that implements the NestInterceptor interface.
  • Implement the intercept method to handle requests and responses.
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    console.log('Before...');
    const now = Date.now();

    return next.handle().pipe(
      map((data) => {
        console.log(`After... ${Date.now() - now}ms`);
        return data;
      }),
    );
  }
}

Adapters

Adapters are used to integrate external libraries or services into the NestJS framework. For example, a WebSocket adapter allows the use of different WebSocket libraries.

Definition:

  • Create a class that implements the WebSocketAdapter interface.
  • Implement methods to handle connections, disconnections, and message events.
import { WebSocketAdapter } from '@nestjs/platform-socket.io';
import * as io from 'socket.io';

export class CustomWebSocketAdapter extends WebSocketAdapter {
  createIOServer(port: number, options?: any): any {
    return io(port, options);
  }
}
Share your love