API with NestJS #26. Real-time chat with WebSockets

JavaScript NestJS TypeScript

This entry is part 26 of 121 in the API with NestJS

With WebSockets, we can perform a two-way communication in real-time between the user and the server. Thanks to that, the browser can send messages to the server and listen to information from the other side.

The principles of the WebSocket handshake

WebSocket is a protocol that operates in a different way than HTTP. Even though that’s the case, establishing the connection begins with the client sending an HTTP call that we call a handshake.

The server listens for incoming socket connections using a regular TCP socket. The client sends a GET request to the URL of our socket.

Request headers:

Above, we can see the and headers. The server understands that the client requests to upgrade the protocol from HTTP to WebSocket. After receiving the above request, the server responds with an indication that the protocol will change from HTTP to WebSocket. The status code of the response is .

Response headers:

In the request, we can also see the header that contains random bytes. The browser adds it to prevent the cache proxy from responding with a previous WebSocket connection. The server hashes the value of the and sends the value through the . Thanks to that, the client can make sure that it got the correct response.

Implementing the chat functionality in NestJS

In the Node.js world, there are two major solutions to implementing WebSockets. The first of them is called was, and it uses bare WebSockets protocol. The other one is socket.io that provides more features through an additional abstraction.

Currently, the implementation of socket.io for NestJS seems to be more popular than the implementation of ws. Therefore, in this article, we use socket.io.

Currently, NestJS does not use the version 3.x of socket.io. Therefore, you need to use the version 2.x of the socket.io-client library on your frontend

The first step in working with WebSockets in NestJS is creating a gateway. Its job is to receive and send messages.

chat.gateway.ts

In this simple example above, we listen to any incoming events. When that happens, we populate this message to all connected clients. Doing that already gives us a straightforward chat functionality.

In the 24th part of this series, we’ve learned how to use a cluster to run multiple instances of our application. If you implement that approach, you might have trouble when using Socket.IO. To deal with it, you would have to use socket.io-redis, as explained in the official documentation.

Authenticating users

The first thing that we would want to add above is authentication. The most straightforward way of approaching it in our current architecture would be to get the authentication token from the cookies.

If you want to know how we implemented the authentication with cookies, check out API with NestJS #3. Authenticating users with bcrypt, Passport, JWT, and cookies

From the first paragraph of this article, we know that the initial handshake is a regular HTTP request. We can access it along with its headers. To parse the cookie, we use the cookie library.

chat.service.ts

Above, we use the method. Let’s implement it also.

authentication.service.ts

To use the method, we need to provide it with the current socket. We can do that in the method of our if it implements the interface.

chat.gateway.ts

We can also use the above to authenticate users when they post messages. To do that, let’s modify our method.

chat.gateway.ts

Now, our users receive both the content of the messages in the chat and the information about the author.

Persisting the messages in the database

So far, we’ve only forwarded incoming messages to all of the connected users. Any new users that join the conversation wouldn’t be able to view its history. To improve that, we need to save all of the messages in the database.

message.entity.ts

We also need to implement the logic of saving and retrieving messages. Let’s do that in our :

chat.service.ts

The last thing is to use the above functionalities in our :

chat.gateway.ts

Our clients need to emit the event as soon as they connect for the above to work.

By returning the object from the method we send and acknowledgment stating that we’ve receive a message correctly.

Summary

In this article, we’ve implemented a chat functionality. To do that, we’ve also learned how WebSockets work and what is a handshake. Although our chat is working, it is still quite basic. For example, it could be improved by adding information about the time of the message. Feel free to experiment and add your own features.

Series Navigation<< API with NestJS #25. Sending scheduled emails with cron and NodemailerAPI with NestJS #27. Introduction to GraphQL. Queries, mutations, and authentication >>
Subscribe
Notify of
guest
9 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Patrick
Patrick
3 years ago

Thank you for article. Will you describe the use of Server-Sent Events with authorization in future?

qetr1ck
qetr1ck
3 years ago

An awesome list!
Would be nice to see Nest.js + GraphQL (code first / schema first) approaches

Vladimir
Vladimir
3 years ago

Thank you a lot for this article. Can we use Nest.js guards to avoid calling getUserFromSocket for each method? What do you think?

puteeen
puteeen
1 year ago
Reply to  Vladimir

Can use custom decorators

jaytonic
jaytonic
2 years ago

Awesome, I’m reading the whole series, very interesting! One thing that I can’t figure out is how to have some kind of “group”. With the “chat” analogy, it would be to have different rooms for different people. Do you need one gateway per room? Or one gateway but your receive parameters indicating the room? but then how do send message back only to users in the room?

Stephanie
Stephanie
2 years ago

how do we use the Chat API ?

Awssam Saidi
Awssam Saidi
2 years ago
Reply to  Stephanie

i have the same question

Mielz
Mielz
2 years ago

This is cool, but how to use the interceptor created in https://dev.wanago.io/2021/08/23/api-nestjs-relationships-mongodb/?

markflerko
2 years ago

Great article, it’ll be nice to show how to test what we’ve in the end of the article (how you usually did)

Thanks