Skip to content
npm version

WebSocket: Backend Message Emit

Overview

Send WebSocket messages to different targets in a type-safe manner using vtzac/typed-emit, supporting scenarios like single client, rooms, global broadcast, etc.

Core Features

Event Definition

Use the @Emit decorator to define events, with method return values as the data structure to be sent:

Event Definition Example:

ts
import { Emit } from 'vtzac/typed-emit';

class ChatEvents {
  @Emit('welcome')
  welcome(nickname: string) {
    return { text: `Welcome ${nickname}!`, timestamp: Date.now() };
  }

  @Emit('message')
  message(text: string) {
    return { text, timestamp: Date.now() };
  }
}

Message Sending

Use the emitWith method to select sending targets:

Target Selection:

ts
import { emitWith } from 'vtzac/typed-emit';

const events = new ChatEvents();

// Send to single client
emitWith(events.welcome, events)('Alice').toClient(client);

// Send to room (excluding current client)
emitWith(events.message, events)('Hello room').toRoom(client, 'room1');

// Send to room (including current client)
emitWith(events.message, events)('Hello all').toRoomAll(server, 'room1');

// Broadcast to entire server
emitWith(events.message, events)('Global message').toServer(server);

Gateway Integration

Complete Example

Backend Gateway Example:

ts
import type { Server, Socket } from 'socket.io';
import {
  ConnectedSocket,
  MessageBody,
  SubscribeMessage,
  WebSocketGateway,
  WebSocketServer,
} from '@nestjs/websockets';
import { emitWith } from 'vtzac/typed-emit';

class ChatEvents {
  @Emit('welcome')
  welcome(nickname: string) {
    return { text: `Welcome ${nickname}!`, timestamp: Date.now() };
  }

  @Emit('message')
  message(text: string) {
    return { text, timestamp: Date.now() };
  }
}

@WebSocketGateway({ cors: { origin: '*' } })
export class ChatGateway {
  private readonly events = new ChatEvents();

  @WebSocketServer()
  server!: Server;

  // Send welcome message when new client connects
  handleConnection(client: Socket) {
    const nickname = `User${client.id.slice(-4)}`;
    emitWith(this.events.welcome, this.events)(nickname).toClient(client);
    // Actual event sent:
    // emit 'welcome' { text: 'Welcome User1234!', timestamp: 1703123456789 }
  }

  // Handle client message and broadcast
  @SubscribeMessage('say')
  handleSay(
    @MessageBody() data: { text: string },
    @ConnectedSocket() client?: Socket
  ) {
    emitWith(this.events.message, this.events)(data.text).toServer(this.server);
    // Actual event sent:
    // Broadcast 'message' { text: 'Hello everyone!', timestamp: 1703123456789 } to all clients
  }

  // Join room and notify
  @SubscribeMessage('joinRoom')
  handleJoinRoom(
    @MessageBody() room: string,
    @ConnectedSocket() client?: Socket
  ) {
    client!.join(room); // Join room first
    emitWith(
      this.events.message,
      this.events
    )(`Joined room ${room}`).toRoomAll(this.server, room);
    // Actual event sent:
    // Send 'message' event to all clients in room 'room1'
  }
}

Quick Start

Minimal Example:

ts
import { Emit, emitWith } from 'vtzac/typed-emit';
import {
  WebSocketGateway,
  SubscribeMessage,
  ConnectedSocket,
} from '@nestjs/websockets';
import type { Socket } from 'socket.io';

class PingEvents {
  @Emit('pong')
  pong() {
    return { message: 'pong', timestamp: Date.now() };
  }
}

@WebSocketGateway({ cors: { origin: '*' } })
export class PingGateway {
  private readonly events = new PingEvents();

  @SubscribeMessage('ping')
  handlePing(@ConnectedSocket() client?: Socket) {
    emitWith(this.events.pong, this.events)().toClient(client!);
    console.log('Sent pong response'); // Output: Sent pong response
  }
}
// Actual WebSocket events:
// Client sends: emit 'ping'
// Server responds: emit 'pong' { message: 'pong', timestamp: 1703123456789 }

Summary

  • Event Definition: Use @Emit decorator to define event names and data structures
  • Message Sending: Select sending targets (client, room, global) through emitWith method
  • Type Safety: Maintain type inference throughout, avoiding manual string and object errors