Nikola Stojaković
Nikola Stojaković

Reputation: 2305

WebSockets on specific route in Nest.js

I'd like to create specific API route which will be used only WebSocket (/api/events) but in all examples of implementing WebSockets on Nest.js I stumbled upon module is imported in AppModule and client is emitting events toward the root URL, which I can't do because I have this middleware;

frontend.middleware.ts

import { Request, Response } from 'express';
import { AppModule } from '../../app.module';

export function FrontendMiddleware(
  req: Request,
  res: Response,
  next: Function,
) {
  const { baseUrl } = req;
  if (baseUrl.indexOf('/api') === 0) {
    next();
  } else {
    res.sendFile('index.html', { root: AppModule.getStaticAssetsRootPath() });
  }
}

Here is the EventGateway and EventModule:

event.gateway.ts

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

@WebSocketGateway({ namespace: 'events' })
export class EventGateway {
  @WebSocketServer()
  server: Server;

  @SubscribeMessage('events')
  findAll(client: Client, data: any): Observable<WsResponse<number>> {
    return from([1, 2, 3]).pipe(map(item => ({ event: 'events', data: item })));
  }

  @SubscribeMessage('identity')
  async identity(client: Client, data: number): Promise<number> {
    return data;
  }
}

event.module.ts

import { Module } from '@nestjs/common';
import { EventGateway } from './event.gateway';

@Module({
  components: [EventGateway],
})
export class EventModule {}

Is there a way to create controller which will allow server-client communication via /api/events?

Upvotes: 8

Views: 13439

Answers (2)

Tobias Oliver Khan
Tobias Oliver Khan

Reputation: 151

Yes, it is possible to create the WebsocketGateway on another path. You can just use the options of the WebsocketGateway to configure the underlying IO-Connection:

E.g:

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

@WebSocketGateway({ path: '/api/events', namespace: 'events' })
export class EventGateway {
  @WebSocketServer()
  server: Server;

  @SubscribeMessage('events')
  findAll(client: Client, data: any): Observable<WsResponse<number>> {
    return from([1, 2, 3]).pipe(map(item => ({ event: 'events', data: item })));
  }

  @SubscribeMessage('identity')
  async identity(client: Client, data: number): Promise<number> {
    return data;
  }
}

This will start the IO-Connection on http://localhost/api/events

Remember to change the connection-path also in your client. It won't be the default /socket.io path anymore, it will be /api/events in your sample.

Upvotes: 15

Mike Drakoulelis
Mike Drakoulelis

Reputation: 789

Websocket is running on the server, not an endpoint. Therefore you cannot have it listen to requests under a specific route, rather just a port, which for Nest's default configuration happens to be the same as the HTTP one.

You could use a reverse proxy like Nginx to redirect the requests towards /api/events facing the Websocket server and also handle the redirection to index.html without changing even the Websocket server's port. Then you would not need the FrontendMiddleware class at all. It is also better since the application does not take the burden of managing request redirections.

Upvotes: -3

Related Questions