JCC Express

Digging Deeper

Broadcasting

Introduction

Broadcasting in JCC Express MVC is event-driven and powered by Socket.IO. The framework creates a shared socketIo server and routes broadcastable events through the global emit(...) helper.

A dispatched event is treated as a broadcast event when it exposes broadcastOn() (and optionally broadcastWith()).


How broadcasting works

emit(new SomeEvent(...)) calls the framework Event.dispatch.

  • If the event has broadcastOn, the event is sent over Socket.IO.
  • Event name is the class name (SomeEvent).
  • Payload is broadcastWith() when provided, otherwise the event instance.
  • Channel behavior:
    • broadcastOn(): "room" -> io.to("room").emit(eventName, payload)
    • broadcastOn(): ["roomA", "roomB"] -> emits to each room
    • falsy channel -> falls back to io.emit(...) (broadcast to all)

Define a broadcast event

Implement broadcastOn() (and optionally broadcastWith()) on your event class:

TypeScript
export class OrderStatusChanged {
  constructor(private order: any) {}

  broadcastOn() {
    return `orders.${this.order.id}`;
  }

  broadcastWith() {
    return {
      id: this.order.id,
      status: this.order.status,
    };
  }
}

You can also return multiple channels:

TypeScript
broadcastOn() {
  return ["admins", `orders.${this.order.id}`];
}

Dispatch from controllers or services

TypeScript
@Method()
async update(order: Order) {
  order.status = "shipped";
  await order.save();

  emit(new OrderStatusChanged(order));
  return { success: true };
}

emit(...) is a global helper registered at application boot, so it is available during app runtime and in tinker.


Automatic initialization

Socket.IO is initialized automatically when the app boots — no manual setup is required. It attaches to the same HTTP server as Express.

Default CORS configuration:

TypeScript
// Applied internally — no config needed for development
cors: { origin: "*" }

For production, restrict the origin inside your SocketProvider using the io instance directly, or configure it at the network/proxy level.


Socket provider setup

The framework initializes Socket.IO in the Express layer. For custom socket listeners/rooms, add a provider that extends SocketProvider:

TypeScript
import { SocketProvider } from "jcc-express-mvc/Core/Provider";

export class SocketIOServiceProvider extends SocketProvider {
  register(): void {}

  boot(): void {
    this.io.on("connection", (socket) => {
      socket.on("join-room", ({ roomId }) => socket.join(roomId));
    });
  }
}

Register the provider in bootstrap/providers.ts.


TypeScript type

When you need to type the Socket.IO server instance in custom code, use the Server type from the socket.io package:

TypeScript
import type { Server } from "socket.io";

constructor(private io: Server) {}

Client-side listening

Listen for the emitted class-name event:

JavaScript
socket.on("OrderStatusChanged", (payload) => {
  console.log(payload);
});

If you emit to a room/channel, ensure the client joined that room first using your socket flow.


Summary

  • Broadcasting is just emit(...) + event classes with broadcastOn().
  • Event class name becomes the Socket.IO event name.
  • broadcastWith() controls the payload shape.
  • Use SocketProvider for connection lifecycle and room logic.
  • Register providers in bootstrap/providers.ts.