Lucas Ballyn
Lucas’ Tech Blog

Lucas’ Tech Blog

Socket IO, rooms and Nest.js; How to?

Lucas Ballyn's photo
Lucas Ballyn

Published on Dec 28, 2020

5 min read

I know my bio says "I do PHP", but sometimes I also do NodeJS, Typescript, or Vue. Or the three combined.

At this moment I'm developing an online quiz program, based on a popular Belgian format called "De Slimste Mens Ter Wereld". Long story short, players have to earn seconds. After some rounds, the one with the most seconds wins. The remaining two play against each other. By giving multiple good answers to a question, the opposing player loses 20 seconds per answer. The first down to zero loses the game.

Why do I need WebSockets with rooms?

Well, when playing the quiz, one admin (the quizmaster) has to control the earned seconds, what question should be shown, the withdrawal of seconds,... Of course, all these changes have to be visible to the other players (the playing players have a different view).

But of course, the applied changes should only be visible to the players that are playing that current quiz. They should not be visible to other quizzes that are played at that moment. So the WebSockets events should only be dispatched to that current Quiz (room).

Technical Information

The quizmaster app, as well as the player's app, is built with Vue js and uses Nest. as back-end. For the coupling between Vue and Nest, I use Inertia.

How to?

In the Vue application, define your WebSocket connection as usual. For this I used VueSocketIO

import VueSocketIO from 'vue-socket.io';
Vue.use(new VueSocketIO({
  connection: 'http://localhost:3000/games',
}))

new Vue({
    el: '#app',
})

Then, in my Component, on mounted I subscribe to the room (which is the UID of the current quiz). This will emit an event to the WebSocket server when the client is connecting.

subscribeToRoom(){
  const self = this;
  this.$socket.on('connect', function() {
    self.$socket.emit('room', self.game.id);
  });
}

In Nest, WebSockets are supported by default and are rather easy to be implemented. See here.

So in the EventGateway, you have to listen to that room event. By implementing the OnGatewayConnection interface we can subscribe to the Websockets lifecycle.

@WebSocketGateway({namespace: '/games'})
export class EventsGateway implements OnGatewayConnection {

    @WebSocketServer()
    server: Server

    handleConnection(client: Socket, room: string): any {
        client.on('room', function(room) {
            client.join(room);
        });
    }
}

Now the room will exist and it will be possible to emit events to that certain room. Where data.room is the Quiz UID. For this example, this is provided through a variable. But this might as well be a header or so.

For example:

@WebSocketGateway({namespace: '/games'})
export class EventsGateway implements OnGatewayConnection {

    @WebSocketServer()
    server: Server

   ...

    setQuestion(data : {}): any {
      this.server.in(data.room).emit('message', {
        'question' : "What do you know about Willam Shakespear?"
      }
    }
}

So now this question will only be dispatched to clients that are "subscribed" to this room, and no other rooms.

Long story short.

  1. In your client, on the connection lifecycle event of your 'Socket IO' instance, submit an event to the server to let them know we want to subscribe to a certain room. this.$socket.emit('room', room name);

  2. On the server, listen to that event and join that room. client.join(room);

  3. When emitting events for a certain room, do this.server.in(room).emit('event', body)

 
Share this