WebSocket cho phép server và client giao tiếp hai chiều theo thời gian thực — lý tưởng cho chat, notification, live dashboard. NestJS tích hợp Socket.io cực kỳ mượt mà thông qua @nestjs/websockets.
1. Cài đặt
npm install --save @nestjs/websockets @nestjs/platform-socket.io socket.io
npm install --save-dev @types/socket.io
2. Tạo Gateway
Trong NestJS, Gateway là tương đương của Controller nhưng cho WebSocket:
nest g gateway chat
src/chat/chat.gateway.ts:
import {
WebSocketGateway,
WebSocketServer,
SubscribeMessage,
MessageBody,
ConnectedSocket,
OnGatewayConnection,
OnGatewayDisconnect,
} from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';
@WebSocketGateway({
cors: { origin: '*' }, // Cấu hình CORS cho client
namespace: '/chat', // Namespace (tùy chọn)
})
export class ChatGateway implements OnGatewayConnection, OnGatewayDisconnect {
@WebSocketServer()
server: Server;
// Khi client kết nối
handleConnection(client: Socket) {
console.log(`Client connected: ${client.id}`);
}
// Khi client ngắt kết nối
handleDisconnect(client: Socket) {
console.log(`Client disconnected: ${client.id}`);
}
// Lắng nghe event 'sendMessage' từ client
@SubscribeMessage('sendMessage')
handleMessage(
@MessageBody() data: { room: string; message: string; username: string },
@ConnectedSocket() client: Socket,
) {
// Broadcast tới tất cả client trong room
this.server.to(data.room).emit('receiveMessage', {
username: data.username,
message: data.message,
timestamp: new Date().toISOString(),
});
}
// Join vào room
@SubscribeMessage('joinRoom')
handleJoinRoom(
@MessageBody() data: { room: string; username: string },
@ConnectedSocket() client: Socket,
) {
client.join(data.room);
// Thông báo cho cả room
this.server.to(data.room).emit('userJoined', {
username: data.username,
message: `${data.username} đã tham gia phòng`,
});
}
// Rời room
@SubscribeMessage('leaveRoom')
handleLeaveRoom(
@MessageBody() data: { room: string; username: string },
@ConnectedSocket() client: Socket,
) {
client.leave(data.room);
this.server.to(data.room).emit('userLeft', {
username: data.username,
message: `${data.username} đã rời phòng`,
});
}
}
3. Đăng ký Gateway vào Module
// src/chat/chat.module.ts
import { Module } from '@nestjs/common';
import { ChatGateway } from './chat.gateway';
@Module({
providers: [ChatGateway],
})
export class ChatModule {}
// src/app.module.ts
import { ChatModule } from './chat/chat.module';
@Module({ imports: [ChatModule] })
export class AppModule {}
4. Emit từ HTTP Controller sang WebSocket
Rất hữu ích khi muốn notify client sau khi có event từ REST API:
import { Injectable } from '@nestjs/common';
import { ChatGateway } from './chat.gateway';
@Injectable()
export class NotificationService {
constructor(private readonly chatGateway: ChatGateway) {}
notifyNewOrder(orderId: string) {
// Gửi event tới TẤT CẢ client đang kết nối
this.chatGateway.server.emit('newOrder', { orderId });
}
notifyRoom(room: string, data: object) {
this.chatGateway.server.to(room).emit('notification', data);
}
}
5. Client-side (JavaScript/TypeScript)
import { io } from 'socket.io-client';
const socket = io('http://localhost:3000/chat');
// Kết nối
socket.on('connect', () => {
console.log('Connected:', socket.id);
// Join room
socket.emit('joinRoom', { room: 'room-1', username: 'Nguyen Van A' });
});
// Gửi tin nhắn
function sendMessage(message) {
socket.emit('sendMessage', {
room: 'room-1',
message,
username: 'Nguyen Van A',
});
}
// Nhận tin nhắn
socket.on('receiveMessage', (data) => {
console.log(`${data.username}: ${data.message}`);
});
// Nhận thông báo user mới vào room
socket.on('userJoined', (data) => {
console.log(data.message);
});
6. Authentication cho WebSocket
Xác thực JWT khi client kết nối:
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { WsException } from '@nestjs/websockets';
import { Socket } from 'socket.io';
@Injectable()
export class WsAuthGuard {
constructor(private jwtService: JwtService) {}
canActivate(client: Socket): boolean {
const token = client.handshake.auth?.token
?? client.handshake.headers?.authorization?.split(' ')[1];
if (!token) throw new WsException('Unauthorized');
try {
const payload = this.jwtService.verify(token);
(client as any).user = payload;
return true;
} catch {
throw new WsException('Invalid token');
}
}
}
Dùng trong Gateway:
handleConnection(client: Socket) {
try {
this.wsAuthGuard.canActivate(client);
console.log(`Authenticated: ${(client as any).user.email}`);
} catch {
client.disconnect();
}
}
7. Kết luận
NestJS + Socket.io giúp xây dựng real-time features dễ dàng:
- Gateway = Controller cho WebSocket
@SubscribeMessage= lắng nghe event từ clientserver.emit= broadcast tới tất cả,server.to(room).emit= gửi tới room cụ thể- Tích hợp tốt với HTTP Controller — notify client sau REST API call
Use cases phổ biến: chat app, live notification, real-time dashboard, collaborative editing.