Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Connect NestJS to a websocket server

How can NestJS be use as a websocket client? I want to connect to a remote websocket server as a client using NestJS, but I didn't find any information about this implementation in the framework.

like image 834
Georgian Stan Avatar asked May 07 '20 07:05

Georgian Stan


People also ask

How do I connect to a WebSocket?

In order to communicate using the WebSocket protocol, you need to create a WebSocket object; this will automatically attempt to open the connection to the server. The URL to which to connect; this should be the URL to which the WebSocket server will respond.

What is a gateway in Nestjs?

In Nest, a gateway is simply a class annotated with @WebSocketGateway() decorator. Technically, gateways are platform-agnostic which makes them compatible with any WebSockets library once an adapter is created.

How do I run a WebSocket server in JavaScript?

To open a websocket connection, we need to create new WebSocket using the special protocol ws in the url: let socket = new WebSocket("ws://javascript.info"); There's also encrypted wss:// protocol. It's like HTTPS for websockets.


2 Answers

As Nestjs is simply a framework for Nodejs, so you need to find an NPM package that supports Websocket. For example, I use ws with @types/ws type definition, and create a Websocket client as a Nestjs service class:

// socket-client.ts
import { Injectable } from "@nestjs/common";
import * as WebSocket from "ws";

@Injectable()
export class WSService {
    // wss://echo.websocket.org is a test websocket server
    private ws = new WebSocket("wss://echo.websocket.org");

    constructor() {
        this.ws.on("open", () => {
            this.ws.send(Math.random())
        });

        this.ws.on("message", function(message) {
            console.log(message);
        });
    }

    send(data: any) {
        this.ws.send(data);
    }

    onMessage(handler: Function) {
        // ...
    }

    // ...
}

// app.module.ts
import { Module } from "@nestjs/common";
import { WSService } from "./socket-client";

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

like image 126
kkkkkkk Avatar answered Sep 21 '22 17:09

kkkkkkk


I try it by another way. I write an adapter with socket.io-client. Then use this adapter in boostrap by method useWebSocketAdapter. After that i can write handle websocket event in gateway like the way working with socket server (use decorator @SubscribeMessage)

My Adapter file

import { WebSocketAdapter, INestApplicationContext } from '@nestjs/common';
import { MessageMappingProperties } from '@nestjs/websockets'

import * as SocketIoClient from 'socket.io-client';
import { isFunction, isNil } from '@nestjs/common/utils/shared.utils';
import { fromEvent, Observable } from 'rxjs';
import { filter, first, map, mergeMap, share, takeUntil } from 'rxjs/operators';

export class IoClientAdapter implements WebSocketAdapter {
    private io;
    constructor(private app: INestApplicationContext) {

    }

    create(port: number, options?: SocketIOClient.ConnectOpts) {
        const client = SocketIoClient("http://localhost:3000" , options || {})
        this.io = client;
        return client;
    }

    bindClientConnect(server: SocketIOClient.Socket, callback: Function) {
        this.io.on('connect', callback);
    }

    bindClientDisconnect(client: SocketIOClient.Socket, callback: Function) {
        console.log("it disconnect")
        //client.on('disconnect', callback);
    }

    public bindMessageHandlers(
        client: any,
        handlers: MessageMappingProperties[],
        transform: (data: any) => Observable<any>,
    ) {
        const disconnect$ = fromEvent(this.io, 'disconnect').pipe(
            share(),
            first(),
        );

        handlers.forEach(({ message, callback }) => {
            const source$ = fromEvent(this.io, message).pipe(
                mergeMap((payload: any) => {
                    const { data, ack } = this.mapPayload(payload);
                    return transform(callback(data, ack)).pipe(
                        filter((response: any) => !isNil(response)),
                        map((response: any) => [response, ack]),
                    );
                }),
                takeUntil(disconnect$),
            );
            source$.subscribe(([response, ack]) => {
                if (response.event) {
                    return client.emit(response.event, response.data);
                }
                isFunction(ack) && ack(response);
            });
        });
    }

    public mapPayload(payload: any): { data: any; ack?: Function } {
        if (!Array.isArray(payload)) {
            return { data: payload };
        }
        const lastElement = payload[payload.length - 1];
        const isAck = isFunction(lastElement);
        if (isAck) {
            const size = payload.length - 1;
            return {
                data: size === 1 ? payload[0] : payload.slice(0, size),
                ack: lastElement,
            };
        }
        return { data: payload };
    }

    close(server: SocketIOClient.Socket) {
        this.io.close()
    }
}

main.js

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import {IoClientAdapter} from './adapters/ioclient.adapter'

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useWebSocketAdapter(new IoClientAdapter(app))
  await app.listen(3006);
  console.log(`Application is running on: ${await app.getUrl()}`);
}
bootstrap();

then Gateway

import {
  MessageBody,
  SubscribeMessage,
  WebSocketGateway,
  WebSocketServer,
  WsResponse,
} from '@nestjs/websockets';
import { from, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { Server } from 'socket.io';

@WebSocketGateway()
export class EventsGateway {
  @WebSocketServer()
  server: Server;

  @SubscribeMessage('hello')
  async identity(@MessageBody() data: number): Promise<number> {
      console.log(data)
    return data;
  }
}

It a trick, but look so cool. Message handler can write more like nestjs style.

like image 41
Kien Nguyen Ngoc Avatar answered Sep 22 '22 17:09

Kien Nguyen Ngoc