NestJS va WebSockets nam 2026: Real-Time, Gateway va Cau hoi Phong van
Huong dan day du ve xay dung ung dung real-time voi NestJS va WebSockets, bao gom gateway, best practices va cac cau hoi phong van ky thuat.

Giao tiep real-time da tro thanh yeu cau thiet yeu trong phat trien ung dung hien dai. Tu ung dung chat, thong bao tuc thi den dashboard giam sat, WebSockets cung cap giai phap hieu qua cho giao tiep hai chieu giua client va server. NestJS, voi tu cach la framework Node.js manh me, cung cap mot abstraction tinh te de lam viec voi WebSockets thong qua khai niem Gateway.
Bai viet nay trinh bay chi tiet ve viec trien khai WebSockets trong NestJS, tu cac khai niem co ban den cac pattern nang cao duoc su dung trong production. Noi dung cung bao gom cac cau hoi phong van ky thuat thuong gap lien quan den chu de nay.
NestJS 11 gioi thieu nhieu cai tien hieu suat dang ke cho WebSocket adapter, bao gom ho tro native cho binary messages va compression. Hay dam bao su dung phien ban moi nhat de tan dung cac tinh nang toi uu.
Tim hieu WebSocket Gateway trong NestJS
Gateway la mot class duoc trang tri bang @WebSocketGateway() va hoat dong nhu diem vao cho cac ket noi WebSocket. Khac voi HTTP controller thong thuong, gateway cho phep giao tiep hai chieu lien tuc giua client va server.
Khai niem gateway trong NestJS lay cam hung tu cac pattern tuong tu trong cac framework khac, nhung tich hop day du vao he sinh thai dependency injection cua NestJS. Dieu nay cho phep su dung cung service, guard, interceptor va pipe nhu trong HTTP controller.
import {
WebSocketGateway,
WebSocketServer,
SubscribeMessage,
MessageBody,
ConnectedSocket,
OnGatewayInit,
OnGatewayConnection,
OnGatewayDisconnect,
} from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';
import { Logger } from '@nestjs/common';
@WebSocketGateway({
cors: {
origin: process.env.ALLOWED_ORIGINS?.split(',') || ['http://localhost:3000'],
credentials: true,
},
namespace: '/events',
transports: ['websocket', 'polling'],
})
export class EventsGateway
implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect
{
@WebSocketServer()
server: Server;
private readonly logger = new Logger(EventsGateway.name);
afterInit(server: Server): void {
this.logger.log('WebSocket Gateway initialized');
}
handleConnection(client: Socket): void {
this.logger.log(`Client connected: ${client.id}`);
}
handleDisconnect(client: Socket): void {
this.logger.log(`Client disconnected: ${client.id}`);
}
@SubscribeMessage('message')
handleMessage(
@MessageBody() data: { content: string; room?: string },
@ConnectedSocket() client: Socket,
): { event: string; data: unknown } {
if (data.room) {
client.to(data.room).emit('message', data);
}
return { event: 'message', data: { received: true, timestamp: Date.now() } };
}
}Doan code tren minh hoa viec trien khai gateway co ban voi cac lifecycle hook. Cac method afterInit, handleConnection va handleDisconnect cung cap quyen kiem soat day du doi voi vong doi ket noi WebSocket.
Cau hinh Adapter: Socket.IO va ws
NestJS ho tro hai adapter chinh cho WebSocket: Socket.IO va ws. Viec lua chon adapter phu thuoc vao yeu cau cu the cua ung dung.
Socket.IO cung cap cac tinh nang bo sung nhu tu dong ket noi lai, quan ly room va fallback sang polling. Adapter ws nhe hon va phu hop cho cac tinh huong can hieu suat toi da voi overhead toi thieu.
import { NestFactory } from '@nestjs/core';
import { IoAdapter } from '@nestjs/platform-socket.io';
import { WsAdapter } from '@nestjs/platform-ws';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// Su dung Socket.IO adapter
app.useWebSocketAdapter(new IoAdapter(app));
// Hoac su dung ws adapter
// app.useWebSocketAdapter(new WsAdapter(app));
await app.listen(3000);
}
bootstrap();Doi voi ung dung can kha nang mo rong theo chieu ngang, Redis adapter la lua chon phu hop. Adapter nay cho phep nhieu instance server chia se trang thai ket noi.
import { IoAdapter } from '@nestjs/platform-socket.io';
import { ServerOptions } from 'socket.io';
import { createAdapter } from '@socket.io/redis-adapter';
import { createClient } from 'redis';
import { INestApplication } from '@nestjs/common';
export class RedisIoAdapter extends IoAdapter {
private adapterConstructor: ReturnType<typeof createAdapter>;
async connectToRedis(): Promise<void> {
const pubClient = createClient({
url: process.env.REDIS_URL || 'redis://localhost:6379',
});
const subClient = pubClient.duplicate();
await Promise.all([pubClient.connect(), subClient.connect()]);
this.adapterConstructor = createAdapter(pubClient, subClient);
}
createIOServer(port: number, options?: ServerOptions): unknown {
const server = super.createIOServer(port, options);
server.adapter(this.adapterConstructor);
return server;
}
}
// Trong main.ts
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const redisIoAdapter = new RedisIoAdapter(app);
await redisIoAdapter.connectToRedis();
app.useWebSocketAdapter(redisIoAdapter);
await app.listen(3000);
}Xac thuc va Phan quyen WebSocket
Bao mat cho ket noi WebSocket doi hoi cach tiep can khac so voi HTTP. Xac thuc thuong duoc thuc hien trong qua trinh handshake ban dau, sau do session duoc duy tri trong suot thoi gian ket noi hoat dong.
import {
WebSocketGateway,
OnGatewayConnection,
WsException,
} from '@nestjs/websockets';
import { Socket } from 'socket.io';
import { JwtService } from '@nestjs/jwt';
import { UsersService } from '../users/users.service';
@WebSocketGateway()
export class AuthenticatedGateway implements OnGatewayConnection {
constructor(
private readonly jwtService: JwtService,
private readonly usersService: UsersService,
) {}
async handleConnection(client: Socket): Promise<void> {
try {
const token =
client.handshake.auth?.token ||
client.handshake.headers?.authorization?.split(' ')[1];
if (!token) {
throw new WsException('Authentication token required');
}
const payload = await this.jwtService.verifyAsync(token);
const user = await this.usersService.findById(payload.sub);
if (!user) {
throw new WsException('User not found');
}
// Luu du lieu user vao socket de truy cap sau
client.data.user = user;
client.join(`user:${user.id}`);
} catch (error) {
client.emit('error', { message: 'Authentication failed' });
client.disconnect();
}
}
}De phan quyen o cap message handler, guard co the duoc su dung tuong tu nhu trong HTTP controller.
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { WsException } from '@nestjs/websockets';
import { Socket } from 'socket.io';
@Injectable()
export class WsAuthGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const client: Socket = context.switchToWs().getClient();
const user = client.data?.user;
if (!user) {
throw new WsException('Unauthorized');
}
return true;
}
}
@Injectable()
export class WsRolesGuard implements CanActivate {
constructor(private readonly requiredRoles: string[]) {}
canActivate(context: ExecutionContext): boolean {
const client: Socket = context.switchToWs().getClient();
const user = client.data?.user;
if (!user || !this.requiredRoles.some((role) => user.roles?.includes(role))) {
throw new WsException('Insufficient permissions');
}
return true;
}
}Trien khai Room va Broadcasting
Room la tinh nang manh me de nhom cac client va gui tin nhan den mot tap hop cu the. Pattern nay rat huu ich cho cac tinh nang nhu phong chat, chinh sua cong tac hoac cap nhat truc tiep theo tung resource.
import {
WebSocketGateway,
WebSocketServer,
SubscribeMessage,
MessageBody,
ConnectedSocket,
} from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';
import { UseGuards } from '@nestjs/common';
import { WsAuthGuard } from './guards/ws-auth.guard';
interface RoomMessage {
roomId: string;
content: string;
type?: 'text' | 'system';
}
@WebSocketGateway()
@UseGuards(WsAuthGuard)
export class RoomsGateway {
@WebSocketServer()
server: Server;
@SubscribeMessage('joinRoom')
async handleJoinRoom(
@MessageBody() data: { roomId: string },
@ConnectedSocket() client: Socket,
): Promise<{ success: boolean; roomId: string }> {
const { roomId } = data;
const user = client.data.user;
await client.join(roomId);
// Thong bao cac thanh vien khac trong room
client.to(roomId).emit('userJoined', {
userId: user.id,
username: user.name,
timestamp: Date.now(),
});
return { success: true, roomId };
}
@SubscribeMessage('leaveRoom')
async handleLeaveRoom(
@MessageBody() data: { roomId: string },
@ConnectedSocket() client: Socket,
): Promise<{ success: boolean }> {
const { roomId } = data;
const user = client.data.user;
await client.leave(roomId);
client.to(roomId).emit('userLeft', {
userId: user.id,
username: user.name,
timestamp: Date.now(),
});
return { success: true };
}
@SubscribeMessage('roomMessage')
handleRoomMessage(
@MessageBody() data: RoomMessage,
@ConnectedSocket() client: Socket,
): void {
const user = client.data.user;
this.server.to(data.roomId).emit('roomMessage', {
...data,
senderId: user.id,
senderName: user.name,
timestamp: Date.now(),
});
}
// Broadcast den tat ca client dang ket noi
broadcastToAll(event: string, payload: unknown): void {
this.server.emit(event, payload);
}
// Broadcast den user cu the (tat ca thiet bi/tab)
broadcastToUser(userId: string, event: string, payload: unknown): void {
this.server.to(`user:${userId}`).emit(event, payload);
}
}Xu ly Loi va Exception Filters
Xu ly loi nhat quan la khia canh quan trong trong ung dung WebSocket. NestJS cung cap exception filter chuyen dung cho WebSocket co the tuy chinh theo nhu cau.
import {
Catch,
ArgumentsHost,
ExceptionFilter,
HttpException,
} from '@nestjs/common';
import { WsException } from '@nestjs/websockets';
import { Socket } from 'socket.io';
interface WsErrorResponse {
status: 'error';
code: string;
message: string;
timestamp: string;
}
@Catch()
export class WsExceptionFilter implements ExceptionFilter {
catch(exception: unknown, host: ArgumentsHost): void {
const client: Socket = host.switchToWs().getClient();
let errorResponse: WsErrorResponse;
if (exception instanceof WsException) {
const error = exception.getError();
errorResponse = {
status: 'error',
code: 'WS_ERROR',
message: typeof error === 'string' ? error : (error as { message: string }).message,
timestamp: new Date().toISOString(),
};
} else if (exception instanceof HttpException) {
errorResponse = {
status: 'error',
code: `HTTP_${exception.getStatus()}`,
message: exception.message,
timestamp: new Date().toISOString(),
};
} else {
errorResponse = {
status: 'error',
code: 'INTERNAL_ERROR',
message: 'An unexpected error occurred',
timestamp: new Date().toISOString(),
};
}
client.emit('exception', errorResponse);
}
}Kiem thu WebSocket Gateway
Kiem thu gateway yeu cau thiet lap dac biet de mo phong ket noi WebSocket. Cac tien ich testing cua NestJS co the ket hop voi socket.io-client de kiem thu toan dien.
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import { IoAdapter } from '@nestjs/platform-socket.io';
import { io, Socket as ClientSocket } from 'socket.io-client';
import { EventsGateway } from './events.gateway';
describe('EventsGateway', () => {
let app: INestApplication;
let clientSocket: ClientSocket;
const PORT = 3001;
beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
providers: [EventsGateway],
}).compile();
app = moduleFixture.createNestApplication();
app.useWebSocketAdapter(new IoAdapter(app));
await app.listen(PORT);
});
afterAll(async () => {
if (clientSocket?.connected) {
clientSocket.disconnect();
}
await app.close();
});
beforeEach((done) => {
clientSocket = io(`http://localhost:${PORT}/events`, {
transports: ['websocket'],
autoConnect: false,
});
clientSocket.connect();
clientSocket.on('connect', done);
});
afterEach(() => {
if (clientSocket?.connected) {
clientSocket.disconnect();
}
});
it('should handle message event', (done) => {
const testData = { content: 'Hello, WebSocket!' };
clientSocket.emit('message', testData, (response: unknown) => {
expect(response).toEqual({
event: 'message',
data: expect.objectContaining({ received: true }),
});
done();
});
});
it('should broadcast to room members', (done) => {
const secondClient = io(`http://localhost:${PORT}/events`, {
transports: ['websocket'],
});
secondClient.on('connect', () => {
// Tham gia room
clientSocket.emit('joinRoom', { roomId: 'test-room' });
secondClient.emit('joinRoom', { roomId: 'test-room' });
setTimeout(() => {
secondClient.on('roomMessage', (data) => {
expect(data.content).toBe('Test message');
secondClient.disconnect();
done();
});
clientSocket.emit('roomMessage', {
roomId: 'test-room',
content: 'Test message',
});
}, 100);
});
});
});Sẵn sàng chinh phục phỏng vấn Node.js / NestJS?
Luyện tập với mô phỏng tương tác, flashcards và bài kiểm tra kỹ thuật.
Cau hoi Phong van Ky thuat
Duoi day la cac cau hoi thuong gap trong phong van ky thuat lien quan den NestJS va WebSockets.
Su khac biet giua HTTP request va WebSocket connection la gi?
HTTP co tinh chat request-response voi ket noi duoc dong sau khi response duoc gui. WebSocket duy tri ket noi lien tuc cho phep giao tiep hai chieu ma khong co overhead handshake lap lai. WebSocket ly tuong cho cac tinh huong real-time can do tre thap va cap nhat lien tuc.
Gateway trong NestJS khac voi Controller nhu the nao?
Controller xu ly HTTP request voi mo hinh request-response truyen thong. Gateway xu ly ket noi WebSocket voi kha nang nhan va gui tin nhan bat dong bo bat ky luc nao trong khi ket noi con hoat dong. Ca hai deu tich hop voi he thong dependency injection cua NestJS.
Khi nao nen su dung Socket.IO va khi nao nen su dung thu vien ws native?
Socket.IO duoc chon khi can cac tinh nang nhu tu dong ket noi lai, quan ly room, namespace va fallback sang long-polling. Thu vien ws native phu hop hon cho cac tinh huong uu tien hieu suat toi da voi giao thuc WebSocket chuan ma khong co abstraction bo sung.
Lam the nao de xu ly xac thuc tren WebSocket?
Xac thuc WebSocket duoc thuc hien trong qua trinh handshake thong qua token trong header hoac query parameter. Token duoc xac thuc trong lifecycle hook handleConnection, va thong tin user duoc luu trong socket.data de truy cap trong cac handler tiep theo. Guard co the duoc su dung de phan quyen theo tung message.
Chien luoc nao de scaling WebSocket server?
Scaling theo chieu ngang yeu cau shared state de phoi hop giua cac instance. Redis adapter cho phep publish-subscribe giua cac server de message co the duoc chuyen tiep den client ket noi o instance khac. Sticky session hoac connection draining cung can duoc xem xet.
Lam the nao de xu ly reconnection o phia client?
Socket.IO cung cap tu dong ket noi lai voi exponential backoff. Ung dung can trien khai state reconciliation khi reconnect, nhu dong bo lai du lieu hoac replay cac event bi bo lo. Server co the luu cac message cho client tam thoi ngat ket noi.
Su khac biet giua emit, broadcast va to la gi?
Method emit gui den socket nguon. Method broadcast.emit gui den tat ca socket ngoai tru nguon. Method to(room).emit gui den tat ca socket trong room cu the. Hieu ro su khac biet nay rat quan trong de trien khai chinh xac.
Ket luan
Trien khai WebSocket trong NestJS cung cap mot abstraction manh me trong khi van duy tri tinh linh hoat can thiet cho nhieu use case khac nhau. Khai niem gateway, ket hop voi cac tinh nang nhu guard, interceptor va pipe, cho phep cac nha phat trien xay dung ung dung real-time de bao tri va co the mo rong.
Viec lua chon adapter, chien luoc xac thuc va pattern cho quan ly room la cac quyet dinh kien truc can duoc dieu chinh theo yeu cau cu the cua ung dung. Kiem thu toan dien va xu ly loi manh me dam bao do tin cay cua ung dung trong production.
Voi su hieu biet sau sac ve cac khai niem nay, developer co the xay dung he thong real-time hieu qua va san sang doi mat voi cac thach thuc phong van ky thuat xung quanh chu de nay.

Viết bởi
Anthony Fillion-MailletLập trình viên fullstack, người sáng lập SharpSkill
Lập trình viên fullstack hơn 10 năm. Anh điều hành SharpSkill và chịu trách nhiệm về mọi nội dung đăng tại đây.
Cập nhật ngày 10 tháng 8, 2026
Thẻ
Chia sẻ
Bài viết liên quan

Microservices với NestJS năm 2026: Kiến trúc, gRPC và Câu hỏi Phỏng vấn
Hướng dẫn toàn diện về kiến trúc microservices NestJS với gRPC: transport layer, Protocol Buffers, streaming patterns và câu hỏi phỏng vấn cho backend engineer năm 2026.

NestJS + Prisma: stack backend hiện đại cho Node.js
Hướng dẫn đầy đủ để xây dựng API backend hiện đại với NestJS và Prisma. Cấu hình, model, service, transaction và các best practice được giải thích chi tiết.

NestJS: Xây dựng REST API hoàn chỉnh từ đầu
Hướng dẫn đầy đủ xây dựng REST API chuyên nghiệp với NestJS. Controller, Service, Module, xác thực dữ liệu với class-validator và xử lý lỗi được giải thích chi tiết.