10

I have a REST API, I want to send event to the client via websocket. How to inject websocket instance in controller or another component?

Raold
  • 1,383
  • 4
  • 20
  • 33
A. Arif
  • 427
  • 1
  • 5
  • 8

3 Answers3

36

Better solution is to create global module. You can then emit events from any other module/controller. A. Afir approach will create multiple instances of Gateway if you try to use it in other modules.

Note: This is just simplest solution

Create socket.module.ts

import { Module, Global } from '@nestjs/common';
import { SocketService } from './socket.service';

@Global()
@Module({
    controllers: [],
    providers: [SocketService],
    exports: [SocketService],
})
export class SocketModule {}

socket.service.ts

import { Injectable } from '@nestjs/common';
import { Server } from 'socket.io';

@Injectable()
export class SocketService {
    public socket: Server = null;
}

app.gateway.ts see afterInit function

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

@WebSocketGateway()
export class AppGateway implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect {

  constructor(private socketService: SocketService){}

  @WebSocketServer() public server: Server;
  private logger: Logger = new Logger('AppGateway');


  afterInit(server: Server) {
    this.socketService.socket = server;
  }

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

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

Then import SocketModule into AppModule and you can use Socket service everywhere.

Thatkookooguy
  • 6,669
  • 1
  • 29
  • 54
Raold
  • 1,383
  • 4
  • 20
  • 33
31

class Gateway can be injected in another component, and use the server instance.

@Controller()
export class AppController {
  constructor(
    private readonly appService: AppService,
    private readonly messageGateway: MessageGateway
  ) {}

  @Get()
  async getHello() {
    this.messageGateway.server.emit('messages', 'Hello from REST API');
    return this.appService.getHello();
  }
}
Kim Kern
  • 54,283
  • 17
  • 197
  • 195
A. Arif
  • 427
  • 1
  • 5
  • 8
  • 1
    Welcome to Stackoverflow! :-) Instead of writing [solved] in your answer text, consider accepting your answer. You may do so after 48h. See https://stackoverflow.com/help/accepted-answer – Kim Kern Jan 18 '19 at 03:23
  • if you want to send an event to a particular cline t using this approach how do you do that?. https://stackoverflow.com/questions/71349200/how-to-pass-socket-client-connection-from-nestjs-service – NicoleZ Mar 05 '22 at 04:05
0

I suppose that @Raold missed a fact in the documentation:

Gateways should not use request-scoped providers because they must act as singletons. Each gateway encapsulates a real socket and cannot be instantiated multiple times.

So it means that we can neither instantiate the gateway class multiple times nor do it explicitly using injection scopes features.

So creating just only one gateway for one namespaces will be right and it will produce only one instance of the websocket or socket.io server.

  • 5
    Thanks for the feedback. I offered better solution for a specific case that works. I don't think this quote is directly related to my example. It would be better if you could explain why this is fundamentally wrong and offer some example. – Raold Jun 23 '21 at 16:56