Reputation: 739
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.
Upvotes: 9
Views: 6762
Reputation: 653
You can extend the existing IoAdapter
.
import { IoAdapter } from '@nestjs/platform-socket.io';
import { INestApplicationContext } from '@nestjs/common';
import { Server, ServerOptions } from 'socket.io';
import { CorsOptions } from 'cors';
export class Adapter extends IoAdapter {
constructor(
appOrHttpServer: INestApplicationContext,
private readonly corsOptions: CorsOptions,
) {
super(appOrHttpServer);
}
create(port: number, options?: ServerOptions): Server {
return super.create(port, {
...options,
cors: this.corsOptions,
});
}
}
Then, you need to use it in the bootstrap
function.
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ConfigService } from '@nestjs/config';
import { Adapter } from './chat/adapter';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const configService = app.get(ConfigService);
app.enableCors({
origin: configService.get('FRONTEND_URL'),
});
app.useWebSocketAdapter(
new Adapter(app, {
origin: configService.get('FRONTEND_URL'),
}),
);
await app.listen(3000);
}
bootstrap();
In my case, the FRONTEND_URL
variable is the URL of my frontend application.
FRONTEND_URL=http://localhost:5173
Upvotes: 0
Reputation: 329
You can use the dotenv package(used by @nestjs/config module) and then import your config file in the websocket module
config/env.ts:
import * as dotenv from 'dotenv';
dotenv.config();
export default () => {
return {
wsPort: parseInt(process.env.WS_PORT),
}
}
events.gateway.ts:
import envSetup from '../config/env';
@WebSocketGateway(envSetup.wsPort)
Upvotes: 0
Reputation: 6404
You can do it relatively straightforward if you decorate the Gateway
before app.init
is called:
main.ts
ConfigurationService
function decorateGateway(class_, config) {
// Just calling the decorator as a function with the class
// as argument does the same as `@WebSocketGateway`
WebSocketGateway({
cors: {
origin: config.get("websocket.cors.origin"),
}
})(class_)
}
async function bootstrap() {
const app = await NestFactory.create(AppModule, {});
const config = app.get(ConfigService);
decorateGateway(ChatGateway, config);
...
app.init();
}
The tricky part with a Gateway
is that it starts up together with the server, and the decorator metadata needs to be applied to the class earlier than for other components. You can do this in main.ts
before app.init
.
Upvotes: 4
Reputation: 81
port = this.configService.get<number>('SOCKETIO.SERVER.PORT');
In my case I found its return string(from .env), port gets 'string' but not 'number',
but if put parseInt(this.configService.get<number>('SOCKETIO.SERVER.PORT'), 10);
then it is ok
Mind that socket-io ports must be the same on server & client side
Upvotes: 0
Reputation: 166
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:
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
Upvotes: 15