Skip to main content

Lab1 - Echo Client

In this lab, we will build the "hello world" of websocket apps, an echo server that will reply any text that a client sends to it.

To get started, checkout the lab1-start branch:

git checkout lab1-start
info

You can find a completed version of this lab in the lab1-complete branch.

Required Dependencies​

This lab is set up with all the dependencies you will require, so there is no need to install anything additional.

Visit the NestJS Websocket guide for more information on setting up Socket.IO in a new project.

Nest Gateway​

Let's start by scaffolding a new gateway in the server folder:

./server
nest generate gateway echo

This will generate an EchoGateway class which looks like:

./server/src/echo.gateway.ts
import { SubscribeMessage, WebSocketGateway } from '@nestjs/websockets';

@WebSocketGateway()
export class EchoGateway {
@SubscribeMessage('message')
handleMessage(client: any, payload: any): string {
return 'Hello world!';
}
}

A gateway is a NestJS abstraction around setting up a real-time communication socket. Gateways are analogous to controllers for API endpoints. They are JavaScript classes that are decorated with @WebSocketGateway(). Under the hood, NestJS is using either the socket-io library (which we use in this course) or the ws library.

The @SubscribeMessage() decorator on a method designates that method as the handler for an incoming message. You pass in the event name string to listen for, which the client also designates when sending a message.

The parameters passed into the method are the socket client and any data sent along with the message. Nest supports Socket.IO as well as the ws project, but it doesn't know which you are using, therefore the method it generates uses type any for the client. We can update this manually to use the Socket.IO Socket class, which is imported from 'socket.io':

./server/src/echo.gateway.ts
import { SubscribeMessage, WebSocketGateway } from '@nestjs/websockets';
import { Socket } from 'socket.io';

@WebSocketGateway()
export class EchoGateway {
@SubscribeMessage('message')
handleMessage(client: Socket, payload: string): string {
return payload;
}
}

To make the gateway "echo" what was sent, return the payload instead of the hard-coded "hello world" string:

./server/src/echo.gateway.ts
@SubscribeMessage('message')
handleMessage(client: Socket, payload: string): string {
return payload;
}

Lastly, we need to enable the websocket to work with clients on other domains by enabling CORS. To do so, we can pass in the option to the @WebSocketGateway() decorator:

./server/src/echo.gateway.ts
@WebSocketGateway({ cors: true })

That's it for the server stuff; let's look at the client next.

Angular​

We'll do this demo in the main app component. Open up app.component.ts and add the following import at the top:

./client/src/app/app.component.ts
import { io } from 'socket.io-client';

The io object creates a new connection to our server and gives us back a client of type Socket that we can use to send messages to the server.

Next, in the AppComponent class, we will create a few variables: a message string that will bind to the text input, a returnedResponses array containing the server's responses, and the Socket.IO socket. We initialize the socket by calling io and passing in the URL of the server.

./client/src/app/app.component.ts
export class AppComponent {
message = '';
returnedResponses: string[] = [];
socket = io('http://localhost:3000');
}

Look at the app now, and open up the dev tools. Go to the WS (for websockets) section of the Network tab. You will see a connection has been established:

Dev Tools WebSocket

Now it's time to send a message across that connection. Back in the app component, add the following sendMessage method to the class:

./client/src/app/app.component.ts
sendMessage() {
this.socket.emit('message', this.message, (msg: string) => {
this.returnedResponses.push(msg)
});
}

The socket emit method takes the event name ('message' in our case), the payload of the event, and a callback. Remember when we returned the payload from handleMessage() in the Nest gateway? That response gets passed back to the callback.

The next step is updating the app component's template with an input box and button to send the message. Replace the welcome message in app.componenent.html with the following:

./client/src/app/app.component.html
<div>
<label
>Message
<input type="text" name="messasge" [(ngModel)]="message" />
</label>
<button type="button" (click)="sendMessage()">Send</button>
<ul>
<li *ngFor="let message of returnedResponses">{{ message }}</li>
</ul>
</div>

Here we have an input bound to message, a button that calls sendMessage when clicked, and a list for each item currently in the returnResponses array.

Now the app echo demo should be fully functional. Give it a try!

Echo Client Running