uWestJS supports all standard NestJS WebSocket decorators for building WebSocket gateways.
Marks a class as a WebSocket gateway.
import { WebSocketGateway } from '@nestjs/websockets';
@WebSocketGateway()
export class ChatGateway {
// Gateway methods
}Note: Gateway options (port, namespace, etc.) are ignored by uWestJS. Configure the adapter directly instead.
Example:
@WebSocketGateway()
export class EventsGateway {
@SubscribeMessage('message')
handleMessage(@MessageBody() data: string) {
return { event: 'response', data };
}
}Marks a method as a message handler for a specific event.
import { SubscribeMessage, MessageBody, ConnectedSocket } from '@nestjs/websockets';
@WebSocketGateway()
export class ChatGateway {
@SubscribeMessage('message')
handleMessage(
@MessageBody() data: string,
@ConnectedSocket() client: any,
) {
return { event: 'response', data: `Echo: ${data}` };
}
}Return Values:
- Return an object to send a response:
{ event: 'response', data: ... } - Return
undefinedorvoidto send no response - Return a Promise for async handlers
Example with async:
@SubscribeMessage('fetch-data')
async handleFetchData(@MessageBody() id: string) {
const data = await this.dataService.findById(id);
return { event: 'data', data };
}Example with no response:
@SubscribeMessage('log-event')
handleLogEvent(@MessageBody() event: string) {
console.log('Event logged:', event);
// No return = no response sent
}Extracts the message data from the incoming message.
@SubscribeMessage('message')
handleMessage(@MessageBody() data: string) {
console.log('Received:', data);
}With validation:
import { UsePipes, ValidationPipe } from '@nestjs/common';
import { IsString, IsNotEmpty } from 'class-validator';
class MessageDto {
@IsString()
@IsNotEmpty()
content: string;
}
@UsePipes(new ValidationPipe())
@SubscribeMessage('message')
handleMessage(@MessageBody() dto: MessageDto) {
console.log('Valid message:', dto.content);
}Extract specific property:
@SubscribeMessage('update-user')
handleUpdate(@MessageBody('userId') userId: string) {
console.log('Updating user:', userId);
}Injects the connected socket instance.
@SubscribeMessage('message')
handleMessage(
@MessageBody() data: string,
@ConnectedSocket() client: UwsSocket,
) {
console.log(`Message from ${client.id}`);
client.emit('response', { received: true });
}Accessing socket data:
@SubscribeMessage('secure-action')
handleSecure(@ConnectedSocket() client: UwsSocket) {
const user = client.data.user;
console.log(`User ${user.name} performed action`);
}Alias for @MessageBody(). Works identically.
import { Payload } from 'uwestjs';
@SubscribeMessage('message')
handleMessage(@Payload() data: string) {
console.log('Received:', data);
}Apply guards to protect message handlers.
import { UseGuards } from '@nestjs/common';
@WebSocketGateway()
export class SecureGateway {
// Method-level guard
@UseGuards(WsAuthGuard)
@SubscribeMessage('secure-action')
handleSecureAction(@MessageBody() data: any) {
return { event: 'success', data };
}
// Multiple guards (executed in order)
@UseGuards(WsAuthGuard, WsRoleGuard)
@SubscribeMessage('admin-action')
handleAdminAction(@MessageBody() data: any) {
return { event: 'admin-success', data };
}
}
// Class-level guard (applies to all handlers)
@UseGuards(WsAuthGuard)
@WebSocketGateway()
export class ProtectedGateway {
@SubscribeMessage('action1')
handleAction1() { }
@SubscribeMessage('action2')
handleAction2() { }
}Apply pipes for validation and transformation.
import { UsePipes, ValidationPipe } from '@nestjs/common';
@WebSocketGateway()
export class ChatGateway {
// Method-level pipe
@UsePipes(new ValidationPipe())
@SubscribeMessage('message')
handleMessage(@MessageBody() dto: MessageDto) {
return { event: 'message-received', data: dto };
}
// Multiple pipes
@UsePipes(ValidationPipe, TransformPipe)
@SubscribeMessage('complex')
handleComplex(@MessageBody() data: any) {
return { event: 'processed', data };
}
}
// Class-level pipe (applies to all handlers)
@UsePipes(new ValidationPipe({ transform: true }))
@WebSocketGateway()
export class ValidatedGateway {
@SubscribeMessage('action1')
handleAction1(@MessageBody() dto: Dto1) { }
@SubscribeMessage('action2')
handleAction2(@MessageBody() dto: Dto2) { }
}Apply exception filters to handle errors.
import { UseFilters } from '@nestjs/common';
@WebSocketGateway()
export class ChatGateway {
// Method-level filter
@UseFilters(WsExceptionFilter)
@SubscribeMessage('message')
handleMessage(@MessageBody() data: string) {
if (!data) {
throw new WsException('Message cannot be empty');
}
return { event: 'success' };
}
// Multiple filters
@UseFilters(WsExceptionFilter, ValidationExceptionFilter)
@SubscribeMessage('complex')
handleComplex(@MessageBody() data: any) {
// Handler logic
}
}
// Class-level filter (applies to all handlers)
@UseFilters(AllExceptionsFilter)
@WebSocketGateway()
export class ProtectedGateway {
@SubscribeMessage('action1')
handleAction1() { }
@SubscribeMessage('action2')
handleAction2() { }
}import {
WebSocketGateway,
SubscribeMessage,
MessageBody,
ConnectedSocket,
OnGatewayInit,
OnGatewayConnection,
OnGatewayDisconnect,
} from '@nestjs/websockets';
import {
UseGuards,
UsePipes,
UseFilters,
ValidationPipe,
} from '@nestjs/common';
import { UwsSocket } from 'uwestjs';
@UseFilters(WsExceptionFilter)
@WebSocketGateway()
export class ChatGateway
implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect
{
afterInit(server: any) {
console.log('Gateway initialized');
}
handleConnection(client: UwsSocket) {
console.log(`Client connected: ${client.id}`);
client.join('lobby');
}
handleDisconnect(client: UwsSocket) {
console.log(`Client disconnected: ${client.id}`);
}
@SubscribeMessage('message')
@UsePipes(new ValidationPipe())
handleMessage(
@MessageBody() dto: MessageDto,
@ConnectedSocket() client: UwsSocket,
) {
client.broadcast.emit('message', {
from: client.id,
content: dto.content,
});
return { event: 'message-sent', data: { id: Date.now() } };
}
@SubscribeMessage('secure-action')
@UseGuards(WsAuthGuard)
handleSecureAction(
@MessageBody() data: any,
@ConnectedSocket() client: UwsSocket,
) {
return { event: 'success', data };
}
}import { IsString, IsNotEmpty, IsInt, Min, Max } from 'class-validator';
class CreateMessageDto {
@IsString()
@IsNotEmpty()
content: string;
@IsInt()
@Min(1)
@Max(5)
priority: number;
}
@WebSocketGateway()
export class MessagesGateway {
@UsePipes(new ValidationPipe({ transform: true }))
@SubscribeMessage('create-message')
handleCreate(@MessageBody() dto: CreateMessageDto) {
// dto is validated and transformed
return { event: 'message-created', data: dto };
}
}@WebSocketGateway()
export class ExampleGateway {
// Multiple @MessageBody decorators with field extraction
// Each extracts a different property from the message data
@SubscribeMessage('complex-handler')
handleComplex(
@MessageBody('userId') userId: string,
@MessageBody('action') action: string,
@ConnectedSocket() client: UwsSocket,
) {
console.log('User ID:', userId);
console.log('Action:', action);
console.log('Client:', client.id);
}
// Alternative: Get full data and destructure
@SubscribeMessage('complex-handler-alt')
handleComplexAlt(
@MessageBody() data: { userId: string; action: string },
@ConnectedSocket() client: UwsSocket,
) {
const { userId, action } = data;
console.log('User ID:', userId);
console.log('Action:', action);
console.log('Client:', client.id);
}
}@UseGuards(WsAuthGuard)
@UsePipes(new ValidationPipe())
@UseFilters(WsExceptionFilter)
@WebSocketGateway()
export class SecureGateway {
@SubscribeMessage('secure-action')
handleSecureAction(@MessageBody() dto: ActionDto) {
// 1. Guard checks authentication
// 2. Pipe validates and transforms data
// 3. Handler executes
// 4. Filter catches any exceptions
return { event: 'success', data: dto };
}
// Override class-level decorators for specific handler
@UseGuards(AdminGuard) // Replaces WsAuthGuard for this handler
@SubscribeMessage('admin-action')
handleAdminAction(@MessageBody() data: any) {
return { event: 'admin-success', data };
}
}@WebSocketGateway()
export class AsyncGateway {
constructor(private dataService: DataService) {}
@SubscribeMessage('fetch-user')
async handleFetchUser(@MessageBody('userId') userId: string) {
const user = await this.dataService.findUser(userId);
return { event: 'user-data', data: user };
}
@SubscribeMessage('batch-operation')
async handleBatch(@MessageBody() ids: string[]) {
const results = await Promise.all(
ids.map(id => this.dataService.process(id))
);
return { event: 'batch-complete', data: results };
}
}import { Observable, interval } from 'rxjs';
import { map, take } from 'rxjs/operators';
@WebSocketGateway()
export class StreamGateway {
@SubscribeMessage('start-stream')
handleStream(): Observable<any> {
return interval(1000).pipe(
take(10),
map(i => ({ event: 'stream-data', data: { count: i } }))
);
}
}