How to implement WebSocket Gateways in NestJS?
WebSocket Gateways in NestJS
NestJS provides first-class support for WebSocket communication through Gateways. Gateways are classes annotated with @WebSocketGateway() that handle real-time bidirectional communication.
Setup
bash
npm install @nestjs/websockets @nestjs/platform-socket.io socket.ioBasic Gateway
typescript
import {
WebSocketGateway,
WebSocketServer,
SubscribeMessage,
MessageBody,
ConnectedSocket,
OnGatewayConnection,
OnGatewayDisconnect,
} from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';
@WebSocketGateway({
cors: { origin: '*' },
namespace: '/chat',
})
export class ChatGateway implements OnGatewayConnection, OnGatewayDisconnect {
@WebSocketServer()
server: Server;
handleConnection(client: Socket) {
console.log('Client connected:', client.id);
}
handleDisconnect(client: Socket) {
console.log('Client disconnected:', client.id);
}
@SubscribeMessage('message')
handleMessage(
@MessageBody() data: { room: string; text: string },
@ConnectedSocket() client: Socket,
) {
// Broadcast to room
this.server.to(data.room).emit('message', {
user: client.id,
text: data.text,
timestamp: new Date(),
});
}
@SubscribeMessage('joinRoom')
handleJoinRoom(
@MessageBody() room: string,
@ConnectedSocket() client: Socket,
) {
client.join(room);
this.server.to(room).emit('userJoined', { userId: client.id });
return { event: 'joinedRoom', data: room };
}
}Register in Module
typescript
@Module({
providers: [ChatGateway, ChatService],
})
export class ChatModule {}Gateway with Authentication
typescript
@WebSocketGateway()
export class ChatGateway implements OnGatewayConnection {
constructor(private readonly authService: AuthService) {}
async handleConnection(client: Socket) {
try {
const token = client.handshake.auth.token;
const user = await this.authService.verifyToken(token);
client.data.user = user;
console.log(`User ${user.name} connected`);
} catch (err) {
client.emit('error', { message: 'Authentication failed' });
client.disconnect();
}
}
@SubscribeMessage('message')
handleMessage(
@MessageBody() data: { text: string },
@ConnectedSocket() client: Socket,
) {
const user = client.data.user;
this.server.emit('message', {
user: user.name,
text: data.text,
});
}
}Using Guards with WebSockets
typescript
@Injectable()
export class WsAuthGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const client = context.switchToWs().getClient<Socket>();
const user = client.data.user;
return !!user;
}
}
@WebSocketGateway()
export class ChatGateway {
@UseGuards(WsAuthGuard)
@SubscribeMessage('message')
handleMessage(@MessageBody() data: any) {
// Only authenticated users reach here
}
}Emitting from Services
typescript
@Injectable()
export class NotificationService {
constructor(
@Inject('CHAT_GATEWAY')
private readonly chatGateway: ChatGateway,
) {}
async notifyUser(userId: string, notification: any) {
this.chatGateway.server
.to(`user:${userId}`)
.emit('notification', notification);
}
}Lifecycle Hooks
| Hook | When |
|---|---|
OnGatewayInit | After gateway is initialized |
OnGatewayConnection | When client connects |
OnGatewayDisconnect | When client disconnects |
Client Example
typescript
import { io } from 'socket.io-client';
const socket = io('http://localhost:3000/chat', {
auth: { token: 'jwt-token-here' },
});
socket.emit('joinRoom', 'general');
socket.emit('message', { room: 'general', text: 'Hello!' });
socket.on('message', (data) => {
console.log(`${data.user}: ${data.text}`);
});Tip: Use NestJS Gateways for structured WebSocket handling with dependency injection, Guards, Interceptors, and Pipes — all the same patterns you use in REST controllers.
Short Answer
Interview readyPremium
A concise answer to help you respond confidently on this topic during an interview.