Khi làm việc với NodeJS – Express, chúng ta triển khai Websocket vô cùng đơn giản và nhanh chóng. Không lẽ khi làm việc với NestJS ta lại mất công setup source node để kết nối socket na. NestJS cũng đã hỗ trợ triển khai Websocket vô cùng mạnh mẽ.

Theo dõi source code tại đâybranch dev nha

Link postman để test cho dễ.

Triển khai websocket với NestJS

Socket quá là quen thuộc với chúng ta rồi nên mình không hướng dẫn cách dùng socket nữa. Mà mình hướng dẫn cách triển khai Websocket và demo một số chức năng mới trên source code bữa giờ của chúng ta thôi.

Ta cài socket vào nha. Do mình đang dùng nestjs 9 nên cài socket version 9 đỡ mất công phát sinh lỗi.

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

Đầu tiên ta tạo event gatewate cùng cấp với AppModule dùng để quản lý socket như connect, listen, emit, disconnect, …

import {
  ConnectedSocket, MessageBody,
  OnGatewayConnection,
  OnGatewayDisconnect,
  OnGatewayInit,
  SubscribeMessage,
  WebSocketGateway,
  WebSocketServer,
} from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';
import { AuthService } from './user/services/auth.service';

@WebSocketGateway({ cors: true })
export class EventGateway
  implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect
{
  @WebSocketServer()
  server: Server;
  constructor(private readonly authService: AuthService) {}

  handleEmitSocket({ data, event, to }) {
    if (to) {
      // this.server.to(to.map((el) => String(el))).emit(event, data);
      this.server.to(to).emit(event, data);
    } else {
      this.server.emit(event, data);
    }
  }

  @SubscribeMessage('message')
  async handleMessage(@ConnectedSocket() socket: Socket, @MessageBody() data) {
    console.log('message', data, socket.id);
    setTimeout(() => {
      this.server.to(socket.data.email + '1').emit('message', data);
    }, 1000);
  }

  // eslint-disable-next-line @typescript-eslint/no-empty-function
  afterInit(socket: Socket): any {}

  async handleConnection(socket: Socket) {
    console.log('connect', socket.id);
    const authHeader = socket.handshake.headers.authorization;
    if (authHeader && (authHeader as string).split(' ')[1]) {
      try {
        socket.data.email = await this.authService.handleVerifyToken(
          (authHeader as string).split(' ')[1],
        );
        socket.join(socket.data.email);
        console.log('connect success', socket.data.email);
      } catch (e) {
        socket.disconnect();
      }
    } else {
      socket.disconnect();
    }
  }

  async handleDisconnect(@ConnectedSocket() socket: Socket) {
    console.log('disconnect', socket.id, socket.data?.email);
  }
}

Ta khởi tạo websocket với WebSocketGateway() decorator

Việc implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect ta sẽ khai báo 3 function tương ứng xử lý cho 3 Lifecycle hooks

Phần kết nối socket chúng ta sẽ áp dụng phần xác thực token. Nghĩa là đăng nhập mới kết nối socket được.

phần WebSocketServer decorator dùng để emit hay xử lý trên server.

Bình thường chúng ta lắng nghe sự kiện ta dùng on. Nhưng trong NestJS ta dùng SubscribeMessage. Phần emit thì vẫn như vậy thôi.

Bên trong phần kết nối socket, ta có sử dụng hàm handleVerifyToken trong AuthService. Bạn nào không nhớ về JWT xem lại bài trước tại đây nha.Ta đi khai báo nó nào:

async handleVerifyToken(token) {
    try {
      const payload = this.jwtService.verify(token); 
      // this.configService.get('SECRETKEY')
      return payload['email'];
    } catch (e) {
      throw new HttpException(
        {
          key: '',
          data: {},
          statusCode: HttpStatus.UNAUTHORIZED,
        },
        HttpStatus.UNAUTHORIZED,
      );
    }
  }

Và phải export AuthService ra thì mới dùng được, chứ không là nó lỗi dependency á

...
@Global()
@Module({
  ...
  exports: [UserService, AuthService],
})
export class UserModule {}

Đừng quên khai báo EventGateway vào AppModule nha

...
import { EventGateway } from './event.gateway';

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

Để test socket, đơn giản là ta tạo 1 file html để kết nối vào socket thôi:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <h1>Hello Socket</h1>
  <script src="https://cdn.socket.io/4.4.1/socket.io.min.js"></script>

  <script>
    var socket = io.connect('http://localhost:3000', {
      extraHeaders: {
        Authorization: "Bearer your_token"
      }
    });
    socket.on('connect', function () {
      console.log('Connected');

      socket.emit('message', {message: "quangdat"})
    });

    socket.on('disconnect', function () {
      console.log('Disconnected');
    });

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

  </script>
</body>
</html>

Các bạn đăng nhập và lấy token đó vào đây, nhớ bật f12 trên trình duyệt để xem log nha.

Kết luận

Qua này này mình đã cơ bạn giới thiệu được các làm việc với Websocket bên trong NestJS. Căn bản socket chỉ có vậy thôi.

Rất mong được sự ủng hộ của mọi người để mình có động lực ra những bài viết tiếp theo.
{\__/}
( ~.~ )
/ > ♥️ I LOVE YOU 3000

JUST DO IT!