Skip to content

Chapter 5 - Create a WebSocket gateway with NestJS and Socket.IO

WebSocket is a protocol that allows bi-directional communications. That means your frontend service can connect to the backend service and both can send/receive data anytime.

This protocol will be used in the drawing application to send all the points drawn by the players to the rest of connected players, in real-time.

Socket.IO is a library that uses WebSocket protocol, enabling the creation of interactive applications with Node.js and the browser.

In this chapter, you'll install and configure a Socket.IO gateway with NestJS and validate it works with the cURL command-line tool.

Info

All the following steps will be executed in the backend directory.

In a new terminal, you can switch to the backend directory with the following command.

In a terminal, execute the following command(s).
cd backend

Steps

Install Socket.IO

Install Socket.IO and NestJS WebSocket support with the following command.

In a terminal, execute the following command(s).
npm install --save @nestjs/websockets @nestjs/platform-socket.io

Create the backend types

Resembling the steps done for the frontend, create a new types directory to store all the types needed by the backend.

In future chapters, you'll improve the management of the types with a shared project. But for now, duplicate the code in both project.

backend/src/types/point.type.ts
1
2
3
4
export type Point = {
	x: number;
	y: number;
};

Create the WebSocket gateway

Create the WebSocket gateway with NestJS.

backend/src/app.gateway.ts
import {
	ConnectedSocket,
	MessageBody,
	OnGatewayConnection,
	OnGatewayDisconnect,
	SubscribeMessage,
	WebSocketGateway,
} from '@nestjs/websockets';
import { Socket } from 'socket.io';
import { Point } from './types/point.type';

@WebSocketGateway({
	cors: {
		origin: '*',
	},
})
export class AppGateway
	implements OnGatewayConnection, OnGatewayDisconnect {

	handleConnection(@ConnectedSocket() socket: Socket): void { // (1)!
		console.log('A player has connected');
	}

	handleDisconnect(@ConnectedSocket() socket: Socket): void { // (2)!
		console.log('A player has disconnected');
	}

	@SubscribeMessage('FIRST_POINT_FROM_PLAYER') // (3)!
	start(@ConnectedSocket() /* (4)! */socket: Socket, @MessageBody() /* (5)! */point: Point): void {
		socket.broadcast.emit('FIRST_POINT_TO_PLAYERS', point); // (6)!
	}

	@SubscribeMessage('POINT_FROM_PLAYER') // (7)!
	addPoint(
		@ConnectedSocket() socket: Socket,
		@MessageBody() point: Point,
	): void {
		socket.broadcast.emit('POINT_TO_PLAYERS', point); // (8)!
	}
}
  1. This method will be called whenever a WebSocket connects to your backend.
  2. This method will be called whenever a WebSocket disconnects from your backend.
  3. This methods will be called whenever a WebSocket sends the event FIRST_POINT_FROM_PLAYER.
  4. The @ConnectedSocket() decorator is able to extract the WebSocket from the request.
  5. The @MessageBody() decorator is able to extract the payload sent by the WebSocket from the request. In this case, a point (point).
  6. The socket broadcasts to all other players connected to your backend the event FIRST_POINT_TO_PLAYERS with the point the player did. They will all receive the event FIRST_POINT_TO_PLAYERS with the point (point).
  7. This method will be called whenever a WebSocket sends the event POINT_FROM_PLAYER.
  8. The socket broadcasts to all other players connected to your backend the event POINT_TO_PLAYERS with the point the player did. They will all receive the event POINT_TO_PLAYERS with the point (point).

Add the WebSocket gateway to NestJS

backend/src/app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppGateway } from './app.gateway';
import { AppService } from './app.service';

@Module({
	controllers: [AppController],
	providers: [AppService, AppGateway],
})
export class AppModule {}

Try out the WebSocket gateway

Start the application.

The output should be similar to this.

> backend@0.0.1 start:dev
> nest start --watch

[4:00:35PM] Starting compilation in watch mode...

[4:00:44PM] Found 0 errors. Watching for file changes.

[Nest] 68566  - 02/09/2023, 4:00:49PM     LOG [NestFactory] Starting Nest application...
[Nest] 68566  - 02/09/2023, 4:00:49PM     LOG [InstanceLoader] AppModule dependencies initialized +26ms
[Nest] 68566  - 02/09/2023, 4:00:50PM     LOG [WebSocketsController] AppGateway subscribed to the "FIRST_POINT_FROM_PLAYER" message +361ms
[Nest] 68566  - 02/09/2023, 4:00:50PM     LOG [WebSocketsController] AppGateway subscribed to the "POINT_FROM_PLAYER" message +0ms
[Nest] 68566  - 02/09/2023, 4:00:50PM     LOG [RoutesResolver] AppController {/}: +2ms
[Nest] 68566  - 02/09/2023, 4:00:50PM     LOG [RouterExplorer] Mapped {/, GET} route +4ms
[Nest] 68566  - 02/09/2023, 4:00:50PM     LOG [NestApplication] Nest application successfully started +4ms

Try to connect to the backend using the WebSocket protocol with cURL.

In a terminal, execute the following command(s).
curl \
	--include \
	--http1.1 \
	--no-buffer \
	--output - \
	--header "Connection: Upgrade" \
	--header "Upgrade: websocket" \
	--header "Host: localhost:4000" \
	--header "Origin: http://localhost:4000" \
	--header "Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==" \
	--header "Sec-WebSocket-Version: 13" \
	"http://localhost:4000/socket.io/?EIO=4&transport=websocket"

The output should be similar to this.

Output of the cURL command.
1
2
3
4
5
6
7
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Access-Control-Allow-Origin: *

�k0{"sid":"GpKbGO7jxR9kSWIQAAAJ","upgrades":[],"pingInterval":25000,"pingTimeout":20000,"maxPayload":1000000}

The command line will not be responsive for ~30 seconds. You can cancel the command by pressing Ctrl+C in your terminal.

No logs will be shown in the NestJS application but cURL can ensure it will work in the next chapter (and you'll see messages on the NestJS side :))

To stop your NestJS application, press Ctrl+C in your terminal.

Summary

Congrats! You now have a NestJS application that can wait for WebSocket connections. Using WebSocket will enable the bi-directional communication between the backend and the frontend in the next chapter.