NestJS i WebSockets w 2026: Komunikacja w Czasie Rzeczywistym, Gateway i Pytania Rekrutacyjne

Kompleksowy przewodnik po WebSockets w NestJS z wzorcem Gateway, integracją Socket.IO, uwierzytelnianiem, obsługą błędów i pytaniami rekrutacyjnymi na 2026 rok.

NestJS i WebSockets w 2026

NestJS WebSockets oferują strukturalne podejście do komunikacji w czasie rzeczywistym przy użyciu wzorca Gateway. W przeciwieństwie do surowych implementacji WebSocket, NestJS abstrahuje złożoność poprzez dekoratory i bezproblemowo integruje się z systemem wstrzykiwania zależności.

Szybka Definicja

WebSocket Gateway w NestJS to klasa ozdobiona dekoratorem @WebSocketGateway(), która obsługuje dwukierunkową komunikację między klientem a serwerem. Obsługuje wiele adapterów (Socket.IO, ws) i integruje się z Guards, Pipes oraz Interceptors w NestJS.

Konfiguracja WebSocket Gateway z Socket.IO

Domyślny adapter w NestJS wykorzystuje Socket.IO, który zapewnia automatyczne ponowne łączenie, obsługę pokoi i fallback do HTTP long-polling. Instalacja wymaga pakietu specyficznego dla platformy wraz z głównym modułem WebSockets.

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

Podstawowy gateway nasłuchuje przychodzących wiadomości i może rozsyłać odpowiedzi do połączonych klientów.

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(),
    });
  }
}

Dekorator @SubscribeMessage wiąże metodę z konkretną nazwą zdarzenia. @MessageBody() wyodrębnia payload, podczas gdy @ConnectedSocket() zapewnia dostęp do gniazda klienta dla ukierunkowanych odpowiedzi.

Hooki Cyklu Życia do Zarządzania Połączeniami

Gatewaye NestJS implementują interfejsy cyklu życia do obsługi połączeń i rozłączeń klientów. Te hooki umożliwiają czyszczenie zasobów, śledzenie obecności i walidację połączeń.

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}`);
  }
}

Trzy interfejsy cyklu życia służą różnym celom: OnGatewayInit uruchamia się raz przy starcie, OnGatewayConnection wyzwala się przy każdym połączeniu klienta, a OnGatewayDisconnect obsługuje czyszczenie gdy klienci opuszczają połączenie.

Uwierzytelnianie z WebSocket Guards

Połączenia WebSocket wymagają walidacji uwierzytelniania przed zezwoleniem na dostęp do chronionych zdarzeń. NestJS Guards współpracują z gatewayami, chociaż kontekst wykonania różni się od żądań HTTP. Oficjalna dokumentacja NestJS WebSockets szczegółowo opisuje integrację guards.

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');
    }
  }
}

Guard stosuje się do klasy gateway lub poszczególnych handlerów wiadomości za pomocą dekoratora @UseGuards(). Klasa WsException rzuca błędy, które klient otrzymuje poprzez zdarzenie error Socket.IO.

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
  }
}

Dla pytań dotyczących uwierzytelniania na poziomie modułu warto zapoznać się z modułem pytań rekrutacyjnych NestJS o uwierzytelnianiu obejmującym strategie JWT i zarządzanie sesjami.

Gotowy na rozmowy o Node.js / NestJS?

Ćwicz z naszymi interaktywnymi symulatorami, flashcards i testami technicznymi.

Wzorce Rozgłaszania Oparte na Pokojach

Pokoje Socket.IO umożliwiają ukierunkowane dostarczanie wiadomości do podzbiorów połączonych klientów. Typowe przypadki użycia obejmują pokoje czatu, lobby gier i funkcje współpracy na żywo.

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);
  }
}

Różnica między client.to(room).emit() a server.to(room).emit() ma znaczenie: pierwsza wyklucza nadawcę, podczas gdy druga obejmuje wszystkich członków pokoju.

Obsługa Błędów z Filtrami Wyjątków

Wyjątki WebSocket wymagają dedykowanych filtrów, ponieważ filtry wyjątków HTTP nie mają zastosowania w kontekstach gateway. Niestandardowe filtry przechwytują WsException i formatują odpowiedzi błędów dla klientów.

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(),
    });
  }
}

Filtry stosuje się na poziomie gateway dla spójnej obsługi błędów we wszystkich handlerach wiadomości.

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
}

Ten wzorzec odzwierciedla filozofię NestJS dotyczącą dekoratorów i filtrów opisaną w module middleware i interceptors.

Skalowanie WebSockets z Adapterem Redis

Wdrożenia WebSocket na pojedynczym serwerze zawodzą, gdy load balancing rozprowadza klientów na wiele instancji. Adapter Redis Socket.IO synchronizuje zdarzenia między instancjami poprzez mechanizm pub/sub.

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 {}

Następnie gateway używa adaptera do komunikacji między instancjami.

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);
  }
}

Z adapterem Redis wiadomość wyemitowana na jednej instancji serwera dociera do klientów połączonych z dowolną instancją w klastrze.

Popularne Pytania Rekrutacyjne dotyczące NestJS WebSockets

Rozmowy techniczne często badają zrozumienie decyzji architektonicznych dotyczących czasu rzeczywistego i szczegółów implementacyjnych specyficznych dla NestJS.

Często Zadawane

Jak NestJS obsługuje uwierzytelnianie WebSocket inaczej niż HTTP?

Middleware HTTP nie wykonuje się dla połączeń WebSocket. Uwierzytelnianie odbywa się podczas fazy handshake lub poprzez Guards zastosowane do gateway. Walidacja tokenów zazwyczaj odbywa się w handleConnection() lub w niestandardowym Guard, który odczytuje z socket.handshake.auth.

Jaka jest różnica między konfiguracją portu @WebSocketGateway() a głównym portem aplikacji?

Domyślnie gatewaye WebSocket współdzielą port serwera HTTP. Określenie portu w @WebSocketGateway(3001) tworzy oddzielny serwer WebSocket na tym porcie. Współdzielone porty upraszczają wdrożenie, ale wymagają routingu opartego na ścieżkach (/socket.io) przy użyciu reverse proxy.

Jak testować gatewaye WebSocket w NestJS?

Narzędzia testowe NestJS wspierają testowanie gateway poprzez Test.createTestingModule(). Biblioteka socket.io-client łączy się z serwerem testowym. Hooki cyklu życia i handlery wiadomości testuje się emitując zdarzenia i sprawdzając odpowiedzi lub efekty uboczne.

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();
    });
  });
});

Kiedy należy używać adaptera ws zamiast Socket.IO?

Adapter biblioteki ws (@nestjs/platform-ws) oferuje mniejszy narzut dla aplikacji, które nie potrzebują funkcji Socket.IO takich jak automatyczne ponowne łączenie, pokoje czy transporty fallback. Systemy handlu wysokiej częstotliwości i serwery gier często preferują ws ze względu na zmniejszone opóźnienia.

Dla szerszych koncepcji architektury NestJS, powiązany artykuł o Guards, Interceptors i Architekturze Modularnej opisuje jak te wzorce integrują się w kontekstach HTTP i WebSocket.

Podsumowanie

  • WebSocket Gateways integrują się z dependency injection NestJS, Guards i Filters dla spójnych wzorców w endpointach HTTP i czasu rzeczywistego
  • Hooki cyklu życia (OnGatewayConnection, OnGatewayDisconnect) zarządzają śledzeniem obecności i czyszczeniem zasobów
  • Przepływy uwierzytelniania różnią się od HTTP; walidacja tokenów odbywa się podczas handshake lub poprzez dedykowane WebSocket Guards
  • Rozgłaszanie oparte na pokojach z Socket.IO umożliwia ukierunkowane wiadomości bez ręcznego zarządzania listami socketów
  • Adapter Redis rozwiązuje skalowanie horyzontalne przez synchronizację zdarzeń między wieloma instancjami serwera
  • Filtry wyjątków wymagają implementacji specyficznych dla WebSocket przy użyciu WsException i niestandardowych filtrów

Zacznij ćwiczyć!

Sprawdź swoją wiedzę z naszymi symulatorami rozmów i testami technicznymi.

Anthony Fillion-Maillet

Autor:

Anthony Fillion-Maillet

Programista fullstack, założyciel SharpSkill

Programista fullstack od ponad 10 lat. Prowadzi SharpSkill i odpowiada za wszystko, co się tu ukazuje.

Zaktualizowano 10 sierpnia 2026

Tagi

#nestjs
#websockets
#real-time
#socket.io
#gateway

Udostępnij

Powiązane artykuły