Search code examples
websocketconfigurationnestjsdecorator

How to pass a dynamic port to the Websockets-gateway in NestJS?


I wanted to dynamically set the Websockets-gateway port from config in NestJS. Below is my websockets-gateway code.

import { WebSocketGateway } from '@nestjs/websockets';

const WS_PORT = parseInt(process.env.WS_PORT);

@WebSocketGateway(WS_PORT)
export class WsGateway {
  constructor() {
    console.log(WS_PORT);
  }
}

But the WS_PORT is always NaN.

This is my bootstrap function insdie main.ts :

async function bootstrap() {
  const app = await NestFactory.create(AppModule, { cors: false });
  const configService = app.get(ConfigService);
  initAdapters(app);
  await app.listen(configService.get(HTTP_PORT), () => {
    console.log('Listening on port ' + configService.get(HTTP_PORT));
  });
}

Below is my app.module.ts :

@Module({
  imports: [
    ConfigModule.forRoot({
      envFilePath: './src/config/dev.env',
      isGlobal: true,
    }),
    RedisModule,
    SocketStateModule,
    RedisPropagatorModule,
    JwtModule.registerAsync({
      imports: [ConfigModule],
      useFactory: async (configService: ConfigService) => ({
        secret: configService.get<string>(JWT_SECRET_KEY),
      }),
      inject: [ConfigService],
    }),
  ],
  controllers: [AppController],
  providers: [WsGateway, AppService],
})
export class AppModule {}

I put a console log in the Gateway constructor to print the value of 'WS_PORT' but it's always NaN.

[Nest] 13252  - 10/04/2021, 5:05:34 PM     LOG [NestFactory] Starting Nest application...
NaN

Thanks in advance.


Solution

  • I could not find a way to add dynamic data to the decorator. So to be able to dynamically choose the port and other configurations I had to:

    • Create an adapter for socket-io:
    • Tell NestJs to use the new adapter

    SocketIoAdapter.ts

    import { INestApplicationContext } from '@nestjs/common';
    import { IoAdapter } from '@nestjs/platform-socket.io';
    import { ServerOptions } from 'socket.io';
    import { ConfigService } from '@nestjs/config';
    
    export class SocketIoAdapter extends IoAdapter {
    constructor(
      private app: INestApplicationContext,
      private configService: ConfigService,
    ) {
      super(app);
    }
    
    createIOServer(port: number, options?: ServerOptions) {
      port = this.configService.get<number>('SOCKETIO.SERVER.PORT');
      const path = this.configService.get<string>('SOCKETIO.SERVER.PATH');
      const origins = this.configService.get<string>(
        'SOCKETIO.SERVER.CORS.ORIGIN',
      );
      const origin = origins.split(',');
      options.path = path;
      options.cors = { origin };
      const server = super.createIOServer(port, options);
      return server;
    }
    }
    

    Now, you need to edit the main.ts to use the adapter

    import { NestFactory } from '@nestjs/core';
    import { AppModule } from './app.module';
    import { ConfigService } from '@nestjs/config';
    import { SocketIoAdapter } from './socket-io/socket-io.adapter';
    
    async function bootstrap() {
      const app = await NestFactory.create(AppModule);
      const configService = app.get(ConfigService);
      const hosts = configService.get<string>('CORS.HOST');
      const hostsArray = hosts.split(',');
      app.enableCors({
        origin: hostsArray,
        credentials: true,
      });
    //Here you use the adapter and sent the config service
      app.useWebSocketAdapter(new SocketIoAdapter(app, configService));
      await app.listen(4300);
    }
    bootstrap();
    
    
    

    In this case I set the port and the cors origin, here an example of the conf file (using .env) env.local

    SOCKETIO.SERVER.PORT=4101
    SOCKETIO.SERVER.PATH=
    SOCKETIO.SERVER.CORS.ORIGIN=http://localhost:4200,http://localhost.com:8080
    

    Here a link to config service Config Service NestJs