typescript 66 lines · 8 steps

Building a real-time chat gateway in NestJS

A NestJS WebSocket gateway that handles connections, room joins, and message broadcasting over Socket.IO.

Explained by highlit
1import {
2 WebSocketGateway,
3 WebSocketServer,
4 SubscribeMessage,
5 MessageBody,
6 ConnectedSocket,
7 OnGatewayConnection,
8 OnGatewayDisconnect,
9} from '@nestjs/websockets';
10import { Server, Socket } from 'socket.io';
11import { Logger, UseGuards } from '@nestjs/common';
12import { WsJwtGuard } from './guards/ws-jwt.guard';
13import { ChatService } from './chat.service';
14import { JoinRoomDto, SendMessageDto } from './dto';
15 
16@WebSocketGateway({
17 namespace: '/chat',
18 cors: { origin: process.env.CLIENT_URL, credentials: true },
19})
20export class ChatGateway implements OnGatewayConnection, OnGatewayDisconnect {
21 @WebSocketServer()
22 private readonly server: Server;
23 
24 private readonly logger = new Logger(ChatGateway.name);
25 
26 constructor(private readonly chatService: ChatService) {}
27 
28 handleConnection(client: Socket) {
29 this.logger.log(`Client connected: ${client.id}`);
30 }
31 
32 handleDisconnect(client: Socket) {
33 this.chatService.markOffline(client.id);
34 this.logger.log(`Client disconnected: ${client.id}`);
35 }
36 
37 @UseGuards(WsJwtGuard)
38 @SubscribeMessage('room:join')
39 async onJoinRoom(
40 @MessageBody() { roomId }: JoinRoomDto,
41 @ConnectedSocket() client: Socket,
42 ) {
43 await client.join(roomId);
44 const history = await this.chatService.recentMessages(roomId);
45 client.emit('room:history', history);
46 client.to(roomId).emit('room:presence', { userId: client.data.userId, online: true });
47 }
48 
49 @UseGuards(WsJwtGuard)
50 @SubscribeMessage('message:send')
51 async onMessage(
52 @MessageBody() dto: SendMessageDto,
53 @ConnectedSocket() client: Socket,
54 ) {
55 const message = await this.chatService.persist({
56 ...dto,
57 authorId: client.data.userId,
58 });
59 this.server.to(dto.roomId).emit('message:new', message);
60 return { status: 'delivered', id: message.id };
61 }
62 
63 broadcastAnnouncement(text: string) {
64 this.server.emit('system:announcement', { text, at: new Date().toISOString() });
65 }
66}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1A gateway maps socket events to methods with decorators, keeping transport wiring out of your handlers.
  2. 2Rooms let you target broadcasts precisely — to one client, everyone but the sender, or the whole namespace.
  3. 3Guards secure individual socket events just like they secure HTTP routes.

Related explainers

typescript
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { ConfigService } from '@nestjs/config';

How a JWT strategy authenticates in NestJS

authentication jwt passport
Intermediate 7 steps
typescript
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse } from '@angular/common/http';
import { BehaviorSubject, Observable, throwError } from 'rxjs';
import { catchError, filter, take, switchMap } from 'rxjs/operators';

Refreshing auth tokens in an Angular interceptor

http-interceptor token-refresh rxjs
Advanced 8 steps
go
package auth
 
import (
	"net/http"

Setting and reading secure session cookies in Go

cookies session-management security
Intermediate 6 steps
typescript
import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common';
import { GqlExecutionContext } from '@nestjs/graphql';
import { Reflector } from '@nestjs/core';
import { JwtService } from '@nestjs/jwt';

How a GraphQL auth guard works in NestJS

authentication authorization jwt
Intermediate 7 steps
typescript
type ResizeCallback = (size: { width: number; height: number }) => void;
 
export function observeResize(callback: ResizeCallback, delay = 150) {
  let timeoutId: ReturnType<typeof setTimeout> | undefined;

Debouncing window resize in TypeScript

debounce closures event-listeners
Intermediate 6 steps
typescript
import { Injectable, Logger, OnApplicationShutdown } from '@nestjs/common';
import { InjectRedis } from '@nestjs-modules/ioredis';
import Redis from 'ioredis';
import { DataSource } from 'typeorm';

Graceful shutdown hooks in NestJS

graceful-shutdown lifecycle-hooks dependency-injection
Intermediate 7 steps

Share this explainer

Here's the card — post it anywhere.

Building a real-time chat gateway in NestJS — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code