Fitur real-time — notifikasi langsung, pengeditan kolaboratif, dashboard status — adalah fitur standar untuk aplikasi web modern. Abstraksi WebSocket Gateway NestJS membuat pembangunan fitur-fitur ini sangat bersih. Saya telah mengimplementasikan sistem notifikasi real-time dan pembaruan dashboard langsung di aplikasi NestJS produksi menggunakan Socket.io, dan abstraksi NestJS menangani 90% boilerplate yang seharusnya Anda tulis sendiri.
NestJS membungkus Socket.io (atau ws) dalam pola decorator Gateway. Gateway secara struktural identik dengan Controller — ia menangani event masuk alih-alih request HTTP. Decorator @WebSocketGateway() mendaftarkan kelas sebagai server WebSocket. @SubscribeMessage('nama-event') menangani event tertentu. @WebSocketServer() menyuntikkan instance server Socket.io yang mendasarinya untuk broadcasting. Gateway terintegrasi penuh dengan dependency injection NestJS, sehingga Anda bisa menyuntikkan layanan, repositori, dan config — persis seperti controller.
Rooms memungkinkan Anda melakukan broadcast ke subset klien yang terhubung — sempurna untuk notifikasi spesifik pengguna atau obrolan grup. Klien bergabung ke room dengan socket.join('room-id'), dan server melakukan broadcast ke sana dengan this.server.to('room-id').emit('event', data). Namespaces memisahkan kekhawatiran di level koneksi — namespace /notifications dan namespace /chat adalah endpoint WebSocket independen.
Single Instance (no Redis) Multi-Instance (with Redis)
─────────────────────────── ──────────────────────────────────
Client A ─── NestJS ──── Client B Client A ─── Instance 1 ─── Redis
│ │
Client B ─── Instance 2 ──────┘
│
Client C ─── Instance 3 ───────┘
All instances subscribe to Redis pub/sub
Any emit → Redis → all instances → target client
NestJS Gateway structure:
@WebSocketGateway({ namespace: '/notifications' })
export class NotificationsGateway implements OnGatewayConnection {
@WebSocketServer() server: Server
handleConnection(socket: Socket) {
const token = socket.handshake.auth.token
// verify JWT, associate socket with userId, join room
socket.join(`user:${userId}`)
}
async sendToUser(userId: string, event: string, data: unknown) {
this.server.to(`user:${userId}`).emit(event, data)
}
}Dari membangun dashboard status pesanan langsung di sistem ERP: emit event hanya untuk data yang klien memiliki izin untuk melihatnya. Gunakan handshake koneksi untuk mengasosiasikan socket dengan ID pengguna dan ID sumber daya yang bisa diakses, lalu pada setiap emit, saring room target berdasarkan izin. Jangan broadcast semua pembaruan pesanan ke semua klien yang terhubung dan saring di frontend — itu membocorkan data bisnis ke pengguna yang tidak berwenang dan membuang bandwidth.
Instance NestJS tunggal menyimpan semua koneksi socket di memori. Ketika Anda melakukan scaling ke beberapa instance di balik load balancer, koneksi socket didistribusikan di seluruh instance. Solusinya adalah Redis sebagai broker pub/sub bersama. Instal paket socket.io-adapter-redis, konfigurasikan dengan koneksi Redis Anda, dan Socket.io menangani broadcasting lintas instance secara otomatis.
# Install packages
npm install --save @nestjs/websockets @nestjs/platform-socket.io socket.io
npm install --save @socket.io/redis-adapter ioredis
# notifications.gateway.ts
import { WebSocketGateway, WebSocketServer, SubscribeMessage, OnGatewayConnection, OnGatewayDisconnect } from '@nestjs/websockets'
import { Server, Socket } from 'socket.io'
import { JwtService } from '@nestjs/jwt'
@WebSocketGateway({
cors: { origin: process.env.FRONTEND_URL, credentials: true },
namespace: '/notifications',
})
export class NotificationsGateway implements OnGatewayConnection, OnGatewayDisconnect {
@WebSocketServer() server: Server
constructor(private jwtService: JwtService) {}
async handleConnection(socket: Socket) {
try {
const token = socket.handshake.auth.token
const payload = this.jwtService.verify(token)
socket.data.userId = payload.sub
await socket.join(`user:${payload.sub}`)
} catch {
socket.disconnect()
}
}
handleDisconnect(socket: Socket) {
// cleanup if needed
}
@SubscribeMessage('ping')
handlePing(socket: Socket) {
return { event: 'pong', data: { timestamp: Date.now() } }
}
// Called by other services to push notifications
async pushToUser(userId: string, notification: NotificationDto) {
this.server.to(`user:${userId}`).emit('notification', notification)
}
}
# main.ts — Redis adapter
import { createAdapter } from '@socket.io/redis-adapter'
import { createClient } from 'redis'
const pubClient = createClient({ url: process.env.REDIS_URL })
const subClient = pubClient.duplicate()
await Promise.all([pubClient.connect(), subClient.connect()])
app.getHttpServer().then((httpServer) => {
const io = require('socket.io')(httpServer)
io.adapter(createAdapter(pubClient, subClient))
})Koneksi WebSocket tidak membawa header setelah upgrade HTTP awal. Kirim JWT dalam opsi auth dari handshake klien Socket.io: io(url, { auth: { token: 'your-jwt' } }). Di sisi Gateway NestJS, implementasikan middleware WebSocket atau gunakan lifecycle hook handleConnection untuk memverifikasi token sebelum mengizinkan koneksi.
Sangat menggoda untuk menyimpan Map<userId, socketId> di kelas Gateway untuk penargetan langsung. Ini rusak begitu Anda memiliki lebih dari satu instance server — peta instance A tidak tahu tentang koneksi di instance B. Selalu gunakan Redis atau database Anda sebagai sumber kebenaran untuk mapping socket-ke-pengguna. Simpan mapping di Redis saat koneksi, hapus saat disconnect, dan cari saat setiap emit.
Koneksi WebSocket terputus — gangguan jaringan, restart server, aplikasi mobile masuk ke background. Klien Socket.io menangani reconnection secara otomatis, tapi Gateway Anda perlu menangani event reconnect dengan anggun: bergabung kembali ke rooms, sinkronisasi ulang state. Di sisi server, implementasikan interface OnGatewayDisconnect untuk membersihkan saat klien disconnect — hapus dari rooms, perbarui status kehadiran di database.
Untuk proyek NestJS apa pun yang membutuhkan fitur real-time, saya menggunakan @nestjs/websockets dengan adapter Socket.io. Abstraksi Gateway NestJS bersih dan terintegrasi sempurna dengan ekosistem NestJS lainnya. Untuk deployment produksi dengan lebih dari satu instance, Redis pub/sub via @socket.io/redis-adapter adalah solusi standar.
Sebelum merilis fitur WebSocket ke produksi: (1) Konfigurasikan Redis adapter untuk deployment multi-instance. (2) Implementasikan autentikasi JWT dalam handshake koneksi. (3) Gunakan rooms untuk broadcast yang dicakup izin, bukan event global. (4) Tangani disconnect dan reconnect dengan anggun. (5) Tetapkan batas koneksi maksimum dan circuit-break jika Redis tidak tersedia. (6) Monitor koneksi socket aktif di stack observabilitas Anda.