Как реализовать WebSockets в NestJS?
WebSockets в NestJS
NestJS предоставляет встроенную поддержку WebSocket-соединений через модуль @nestjs/websockets. Основная абстракция — Gateway (шлюз), который аналогичен контроллеру, но работает с событийной моделью вместо HTTP-запросов.
Установка зависимостей
Для работы с socket.io (наиболее распространённый вариант):
npm install @nestjs/websockets @nestjs/platform-socket.io socket.io
Для нативных WebSocket без socket.io:
npm install @nestjs/websockets @nestjs/platform-ws ws
Создание Gateway
Gateway — это класс, помеченный декоратором @WebSocketGateway(). Он может принимать порт и опции, включая namespace и CORS-настройки.
Жизненный цикл и интерфейсы
Gateway реализует интерфейсы:
OnGatewayInit— вызывается после инициализации (afterInit)OnGatewayConnection— вызывается при подключении клиента (handleConnection)OnGatewayDisconnect— вызывается при отключении (handleDisconnect)
Отправка сообщений
Для отправки событий всем подключённым клиентам используется декоратор @WebSocketServer(), который инжектирует объект сервера. Метод server.emit() транслирует событие всем клиентам.
Для ответа только отправителю метод с @SubscribeMessage может возвращать объект { event, data } или использовать client.emit().
Интеграция с модульной системой
Gateway регистрируется в providers модуля так же, как сервис. Он может инжектировать другие сервисы через конструктор.
Namespace и Room
Socket.io-адаптер поддерживает namespace через @WebSocketGateway({ namespace: '/chat' }) и комнаты через client.join('room-name') / server.to('room-name').emit().
Guards, Pipes и Interceptors
WebSocket Gateway поддерживает стандартные механизмы NestJS: @UseGuards(), @UsePipes(), @UseInterceptors(). Это позволяет переиспользовать логику авторизации и валидации из HTTP-части приложения.
Адаптеры
По умолчанию NestJS использует socket.io адаптер. Для замены на нативный ws нужно в main.ts вызвать app.useWebSocketAdapter(new WsAdapter(app)).
Что хочет услышать интервьюер
Знание декоратора @WebSocketGateway и его параметров (порт, namespace, cors)
Понимание @SubscribeMessage для обработки входящих событий и @WebSocketServer для доступа к серверу
Знание lifecycle-интерфейсов: OnGatewayConnection, OnGatewayDisconnect, OnGatewayInit
Умение интегрировать Gateway в модульную систему NestJS (регистрация в providers)
Понимание разницы между адаптерами socket.io и ws, и когда применять каждый
Пример: Базовый Gateway с socket.io
import {
WebSocketGateway,
WebSocketServer,
SubscribeMessage,
MessageBody,
ConnectedSocket,
OnGatewayConnection,
OnGatewayDisconnect,
} from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';
@WebSocketGateway({
cors: { origin: '*' }, // разрешаем подключения с любого origin
namespace: '/chat',
})
export class ChatGateway implements OnGatewayConnection, OnGatewayDisconnect {
@WebSocketServer()
server: Server; // инжектируем объект сервера socket.io
handleConnection(client: Socket) {
console.log(`Клиент подключился: ${client.id}`);
}
handleDisconnect(client: Socket) {
console.log(`Клиент отключился: ${client.id}`);
}
@SubscribeMessage('sendMessage')
handleMessage(
@MessageBody() data: { room: string; text: string },
@ConnectedSocket() client: Socket,
): void {
// транслируем сообщение всем в комнате, кроме отправителя
client.to(data.room).emit('newMessage', {
from: client.id,
text: data.text,
});
}
@SubscribeMessage('joinRoom')
handleJoinRoom(
@MessageBody() room: string,
@ConnectedSocket() client: Socket,
): void {
client.join(room);
// уведомляем всех в комнате о новом участнике
this.server.to(room).emit('userJoined', { userId: client.id });
}
}
Пример: Регистрация Gateway в модуле
import { Module } from '@nestjs/common';
import { ChatGateway } from './chat.gateway';
import { ChatService } from './chat.service';
@Module({
providers: [ChatGateway, ChatService], // Gateway регистрируется как провайдер
})
export class ChatModule {}
Пример: Gateway с инжекцией сервиса и авторизацией через Guard
import { UseGuards } from '@nestjs/common';
import {
WebSocketGateway,
WebSocketServer,
SubscribeMessage,
MessageBody,
} from '@nestjs/websockets';
import { Server } from 'socket.io';
import { WsJwtGuard } from '../auth/ws-jwt.guard';
import { ChatService } from './chat.service';
@WebSocketGateway({ cors: { origin: '*' } })
export class ChatGateway {
@WebSocketServer()
server: Server;
constructor(private readonly chatService: ChatService) {}
@UseGuards(WsJwtGuard) // защищаем событие JWT-гвардом
@SubscribeMessage('privateMessage')
async handlePrivateMessage(
@MessageBody() payload: { to: string; text: string },
): Promise<void> {
const saved = await this.chatService.saveMessage(payload);
// отправляем сообщение конкретному сокету по id
this.server.to(payload.to).emit('privateMessage', saved);
}
}
Типичные ошибки
Забывают зарегистрировать Gateway в providers модуля — шлюз не поднимается
Путают порт Gateway с портом HTTP-сервера: по умолчанию Gateway слушает отдельный порт, что вызывает CORS-проблемы
Не реализуют OnGatewayDisconnect и не чистят ресурсы (комнаты, активные сессии) при отключении клиента
Используют server.emit() внутри синхронного кода без ожидания готовности сервера вместо afterInit
Смешивают нативный WebSocket API (ws) с socket.io-клиентом на фронтенде — они несовместимы


