NestJS and WebSockets in 2026: Real-Time, Gateway and Interview Questions

Master WebSockets in NestJS with Gateway patterns, Socket.IO integration, authentication, error handling and common interview questions for 2026.

NestJS WebSocket real-time communication gateway

NestJS WebSockets provide a structured approach to real-time communication using the Gateway pattern. Unlike raw WebSocket implementations, NestJS abstracts complexity through decorators and integrates seamlessly with the dependency injection system.

Quick Definition

A WebSocket Gateway in NestJS is a class decorated with @WebSocketGateway() that handles bidirectional communication between client and server. It supports multiple adapters (Socket.IO, ws) and integrates with NestJS Guards, Pipes, and Interceptors.

Setting Up a WebSocket Gateway with Socket.IO

The default adapter in NestJS uses Socket.IO, which provides automatic reconnection, room support, and fallback to HTTP long-polling. Installation requires the platform-specific package alongside the core WebSockets module.

bash
# terminal
npm install @nestjs/websockets @nestjs/platform-socket.io socket.io

A basic gateway listens for incoming messages and can broadcast responses to connected clients.

chat.gateway.tstypescript
import {
  WebSocketGateway,
  WebSocketServer,
  SubscribeMessage,
  MessageBody,
  ConnectedSocket,
} from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';

@WebSocketGateway({
  cors: { origin: '*' }, // Configure CORS for browser clients
})
export class ChatGateway {
  @WebSocketServer()
  server: Server; // Access to the underlying Socket.IO server

  @SubscribeMessage('message') // Listen for 'message' events
  handleMessage(
    @MessageBody() data: { text: string; room: string },
    @ConnectedSocket() client: Socket,
  ): void {
    // Broadcast to all clients in the room except sender
    client.to(data.room).emit('message', {
      text: data.text,
      senderId: client.id,
      timestamp: Date.now(),
    });
  }
}

The @SubscribeMessage decorator binds a method to a specific event name. The @MessageBody() extracts the payload, while @ConnectedSocket() provides access to the client socket for targeted responses.

Lifecycle Hooks for Connection Management

NestJS gateways implement lifecycle interfaces to handle client connections and disconnections. These hooks enable resource cleanup, presence tracking, and connection validation.

presence.gateway.tstypescript
import {
  WebSocketGateway,
  OnGatewayConnection,
  OnGatewayDisconnect,
  OnGatewayInit,
} from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';
import { Logger } from '@nestjs/common';

@WebSocketGateway()
export class PresenceGateway
  implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect
{
  private readonly logger = new Logger(PresenceGateway.name);
  private connectedUsers = new Map<string, { joinedAt: Date }>();

  afterInit(server: Server): void {
    // Called once when the gateway initializes
    this.logger.log('WebSocket Gateway initialized');
  }

  handleConnection(client: Socket): void {
    // Track new connections
    this.connectedUsers.set(client.id, { joinedAt: new Date() });
    this.logger.log(`Client connected: ${client.id}`);
  }

  handleDisconnect(client: Socket): void {
    // Cleanup on disconnect
    this.connectedUsers.delete(client.id);
    this.logger.log(`Client disconnected: ${client.id}`);
  }
}

The three lifecycle interfaces serve distinct purposes: OnGatewayInit runs once at startup, OnGatewayConnection triggers per client connection, and OnGatewayDisconnect handles cleanup when clients leave.

Authentication with WebSocket Guards

WebSocket connections require authentication validation before allowing access to protected events. NestJS Guards work with gateways, though the execution context differs from HTTP requests. The official NestJS WebSockets documentation covers guard integration in detail.

ws-auth.guard.tstypescript
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { WsException } from '@nestjs/websockets';
import { JwtService } from '@nestjs/jwt';
import { Socket } from 'socket.io';

@Injectable()
export class WsAuthGuard implements CanActivate {
  constructor(private jwtService: JwtService) {}

  canActivate(context: ExecutionContext): boolean {
    const client: Socket = context.switchToWs().getClient();
    // Extract token from handshake auth or query params
    const token =
      client.handshake.auth?.token ||
      client.handshake.query?.token;

    if (!token) {
      throw new WsException('Missing authentication token');
    }

    try {
      const payload = this.jwtService.verify(token as string);
      // Attach user data to socket for later use
      client.data.user = payload;
      return true;
    } catch {
      throw new WsException('Invalid token');
    }
  }
}

Apply the guard to the gateway class or individual message handlers using the @UseGuards() decorator. The WsException class throws errors that the client receives through the Socket.IO error event.

secure-chat.gateway.tstypescript
import { UseGuards } from '@nestjs/common';
import { WebSocketGateway, SubscribeMessage } from '@nestjs/websockets';
import { WsAuthGuard } from './ws-auth.guard';

@WebSocketGateway()
@UseGuards(WsAuthGuard) // Protect all handlers in this gateway
export class SecureChatGateway {
  @SubscribeMessage('privateMessage')
  handlePrivateMessage(): void {
    // Only authenticated clients reach this handler
  }
}

For module-level authentication questions, explore the NestJS authentication interview module covering JWT strategies and session management.

Ready to ace your Node.js / NestJS interviews?

Practice with our interactive simulators, flashcards, and technical tests.

Room-Based Broadcasting Patterns

Socket.IO rooms enable targeted message delivery to subsets of connected clients. Common use cases include chat rooms, game lobbies, and live collaboration features.

room.gateway.tstypescript
import {
  WebSocketGateway,
  SubscribeMessage,
  ConnectedSocket,
  MessageBody,
  WebSocketServer,
} from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';

@WebSocketGateway()
export class RoomGateway {
  @WebSocketServer()
  server: Server;

  @SubscribeMessage('joinRoom')
  handleJoinRoom(
    @MessageBody() roomId: string,
    @ConnectedSocket() client: Socket,
  ): { event: string; data: string } {
    // Add client to the specified room
    client.join(roomId);
    // Notify others in the room
    client.to(roomId).emit('userJoined', { userId: client.id });
    return { event: 'joinedRoom', data: roomId };
  }

  @SubscribeMessage('leaveRoom')
  handleLeaveRoom(
    @MessageBody() roomId: string,
    @ConnectedSocket() client: Socket,
  ): void {
    client.leave(roomId);
    client.to(roomId).emit('userLeft', { userId: client.id });
  }

  @SubscribeMessage('roomBroadcast')
  handleRoomBroadcast(
    @MessageBody() payload: { roomId: string; message: string },
  ): void {
    // Send to all clients in room, including sender
    this.server.to(payload.roomId).emit('roomMessage', payload.message);
  }
}

The distinction between client.to(room).emit() and server.to(room).emit() matters: the former excludes the sender, while the latter includes all room members.

Error Handling with Exception Filters

WebSocket exceptions require dedicated filters since HTTP exception filters do not apply to gateway contexts. Custom filters catch WsException and format error responses for clients.

ws-exception.filter.tstypescript
import { Catch, ArgumentsHost, ExceptionFilter } from '@nestjs/common';
import { WsException } from '@nestjs/websockets';
import { Socket } from 'socket.io';

@Catch(WsException)
export class WsExceptionFilter implements ExceptionFilter {
  catch(exception: WsException, host: ArgumentsHost): void {
    const client: Socket = host.switchToWs().getClient();
    const error = exception.getError();
    // Send structured error to client
    client.emit('error', {
      type: 'WsException',
      message: typeof error === 'string' ? error : error,
      timestamp: new Date().toISOString(),
    });
  }
}

Apply filters at the gateway level for consistent error handling across all message handlers.

filtered.gateway.tstypescript
import { UseFilters } from '@nestjs/common';
import { WebSocketGateway } from '@nestjs/websockets';
import { WsExceptionFilter } from './ws-exception.filter';

@WebSocketGateway()
@UseFilters(WsExceptionFilter)
export class FilteredGateway {
  // All handlers benefit from centralized error handling
}

This pattern mirrors the NestJS philosophy of decorators and filters described in the middleware and interceptors module.

Scaling WebSockets with Redis Adapter

Single-server WebSocket deployments fail when load balancing distributes clients across multiple instances. The Socket.IO Redis adapter synchronizes events across instances through a pub/sub mechanism.

app.module.tstypescript
import { Module } from '@nestjs/common';
import { createAdapter } from '@socket.io/redis-adapter';
import { createClient } from 'redis';
import { ChatGateway } from './chat.gateway';

@Module({
  providers: [
    ChatGateway,
    {
      provide: 'REDIS_ADAPTER',
      useFactory: async () => {
        const pubClient = createClient({ url: process.env.REDIS_URL });
        const subClient = pubClient.duplicate();
        await Promise.all([pubClient.connect(), subClient.connect()]);
        return createAdapter(pubClient, subClient);
      },
    },
  ],
})
export class AppModule {}

The gateway then uses the adapter for cross-instance communication.

scalable.gateway.tstypescript
import {
  WebSocketGateway,
  WebSocketServer,
  OnGatewayInit,
} from '@nestjs/websockets';
import { Inject } from '@nestjs/common';
import { Server } from 'socket.io';
import { Adapter } from 'socket.io-adapter';

@WebSocketGateway()
export class ScalableGateway implements OnGatewayInit {
  @WebSocketServer()
  server: Server;

  constructor(@Inject('REDIS_ADAPTER') private redisAdapter: Adapter) {}

  afterInit(): void {
    // Attach Redis adapter for multi-instance support
    this.server.adapter(this.redisAdapter as any);
  }
}

With the Redis adapter, a message emitted on one server instance reaches clients connected to any instance in the cluster.

Common Interview Questions on NestJS WebSockets

Technical interviews often probe understanding of real-time architecture decisions and NestJS-specific implementation details.

Frequently Asked

How does NestJS handle WebSocket authentication differently from HTTP?

HTTP middleware does not execute for WebSocket connections. Authentication happens during the handshake phase or through Guards applied to the gateway. Token validation typically occurs in handleConnection() or a custom Guard that reads from socket.handshake.auth.

What is the difference between @WebSocketGateway() port configuration and the main application port?

By default, WebSocket gateways share the HTTP server port. Specifying a port in @WebSocketGateway(3001) creates a separate WebSocket server on that port. Shared ports simplify deployment but require path-based routing (/socket.io) when using reverse proxies.

How do you test WebSocket gateways in NestJS?

NestJS testing utilities support gateway testing through Test.createTestingModule(). The socket.io-client library connects to the test server. Lifecycle hooks and message handlers are tested by emitting events and asserting on responses or side effects.

chat.gateway.spec.tstypescript
import { Test } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import { io, Socket } from 'socket.io-client';
import { ChatGateway } from './chat.gateway';

describe('ChatGateway', () => {
  let app: INestApplication;
  let client: Socket;

  beforeAll(async () => {
    const module = await Test.createTestingModule({
      providers: [ChatGateway],
    }).compile();

    app = module.createNestApplication();
    await app.listen(3000);
    client = io('http://localhost:3000');
  });

  afterAll(async () => {
    client.disconnect();
    await app.close();
  });

  it('receives message acknowledgment', (done) => {
    client.emit('message', { text: 'test', room: 'lobby' });
    client.on('message', (data) => {
      expect(data.text).toBe('test');
      done();
    });
  });
});

When should you use the ws adapter instead of Socket.IO?

The ws library adapter (@nestjs/platform-ws) offers lower overhead for applications that do not need Socket.IO features like automatic reconnection, rooms, or fallback transports. High-frequency trading systems and game servers often prefer ws for reduced latency.

For broader NestJS architecture concepts, the related article on Guards, Interceptors and Modular Architecture covers how these patterns integrate across HTTP and WebSocket contexts.

Conclusion

  • WebSocket Gateways integrate with NestJS dependency injection, Guards, and Filters for consistent patterns across HTTP and real-time endpoints
  • Lifecycle hooks (OnGatewayConnection, OnGatewayDisconnect) manage presence tracking and resource cleanup
  • Authentication flows differ from HTTP; token validation happens during handshake or through dedicated WebSocket Guards
  • Room-based broadcasting with Socket.IO enables targeted messaging without managing socket lists manually
  • Redis adapter solves horizontal scaling by synchronizing events across multiple server instances
  • Exception filters require WebSocket-specific implementations using WsException and custom filters

Start practicing!

Test your knowledge with our interview simulators and technical tests.

Share

Related articles