AgentStack
SKILL verified MIT Self-run

Websocket Broadcasting

skill-softtor-nestjs-hexagonal-websocket-broadcasting · by Softtor

Use when broadcasting domain events to the frontend via WebSocket (Socket.IO). Covers WsGatewayPort abstraction, gateway with Redis adapter, JWT auth, room-based multi-tenant isolation, and frontend consumption. One simple pattern — no over-engineering.

No reviews yet
0 installs
0 views
view→install

Install

$ agentstack add skill-softtor-nestjs-hexagonal-websocket-broadcasting

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README — it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-softtor-nestjs-hexagonal-websocket-broadcasting)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
4mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming — see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps — measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Websocket Broadcasting? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

WebSocket Broadcasting

Broadcasts domain events to connected frontend clients via Socket.IO. Simple pattern: @EventsHandler -> enrich if needed -> WsGatewayPort.emit().


Flow

Entity.apply(event)            entity.commit()           @EventsHandler          (optional) repo.findById() to enrich payload
      -> gateway.emitToOrganization(orgId, event, data)
        -> Socket.IO server.to(`org:${orgId}`).emit()
          -> Redis Pub/Sub (multi-pod)
            -> connected clients

1. WsGatewayPort (hexagonal abstraction)

Define in shared/ports/ or the BC's application/ports/:

export const WS_GATEWAY_TOKEN = Symbol('WsGateway');

export interface WsGatewayPort {
  emitToOrganization(orgId: string, event: string, data: Record): void;
  emitToUser(userId: string, event: string, data: Record): void;
  emitGlobal(event: string, data: Record): void;
}

Register in module: { provide: WS_GATEWAY_TOKEN, useExisting: AppGateway }

See references/ws-gateway-port.md for full implementation + mock for testing.


2. Bridge Handler (the only pattern you need)

For each domain event that needs to reach the frontend, create an @EventsHandler:

@EventsHandler(OrderCreatedEvent)
export class OrderCreatedBroadcastHandler implements IEventHandler {
  private readonly logger = new Logger(OrderCreatedBroadcastHandler.name);

  constructor(
    @Inject(WS_GATEWAY_TOKEN) private readonly gateway: WsGatewayPort,
    @Inject(ORDER_REPOSITORY) private readonly repo: OrderRepository.Repository,
  ) {}

  async handle(event: OrderCreatedEvent): Promise {
    try {
      // Enrich if needed (optional — skip if event payload is sufficient)
      const order = await this.repo.findById(event.aggregateId);
      if (!order) return;

      this.gateway.emitToOrganization(event.organizationId, 'order:created', {
        id: order.id,
        total: order.total,
        status: order.status,
      });
    } catch (error) {
      // Log but never re-throw — don't break the event chain
      this.logger.error(`Failed to broadcast order:created`, error);
    }
  }
}

Rules:

  • One handler per event that needs broadcasting
  • Wrap in try/catch — never let broadcast failure break the domain flow
  • Enrich from DB only when event payload is insufficient for the frontend
  • If event payload IS sufficient, just forward it directly (no DB call)
  • Event naming: : (e.g., order:created, payment:refunded)

3. Gateway Implementation

See references/gateway-patterns.md for full template. Key points:

@WebSocketGateway({ namespace: '/events', cors: { origin: true, credentials: true } })
export class AppGateway implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect, WsGatewayPort {
  @WebSocketServer() server!: Namespace;

  // JWT auth on connection
  async handleConnection(client: Socket): Promise {
    const token = client.handshake.auth?.token;
    // verify JWT, extract orgId/userId, join rooms
    client.join(`org:${orgId}`);
    client.join(`user:${userId}`);
  }

  // Emit methods (implement WsGatewayPort)
  emitToOrganization(orgId: string, event: string, data: Record): void {
    this.server.to(`org:${orgId}`).emit(event, data);
  }

  emitToUser(userId: string, event: string, data: Record): void {
    this.server.to(`user:${userId}`).emit(event, data);
  }

  emitGlobal(event: string, data: Record): void {
    this.server.emit(event, data);
  }
}

Redis adapter for multi-pod: @socket.io/redis-adapter with pub/sub clients.


4. Room System (multi-tenant isolation)

| Room | Scope | Example | |------|-------|---------| | org:${organizationId} | Organization-wide events | Order created, payment received | | user:${userId} | User-specific events | Notifications, DMs, assignments |

Auto-joined on connection after JWT verification. No manual room management needed.


5. Frontend Consumption

See references/frontend-consumption.md for full patterns. Quick example:

// React hook
function useSocket(event: string, handler: (data: T) => void) {
  useEffect(() => {
    socket.on(event, handler);
    return () => { socket.off(event, handler); };
  }, [event, handler]);
}

// Usage
useSocket('order:created', (data) => {
  queryClient.invalidateQueries(['orders']);
});

Anti-patterns

| Don't | Do | |-------|-----| | Generic relay with @OnEvent('**') | Explicit handler per event | | Custom broadcast event pattern | Direct @EventsHandler | | Event mapping tables | Handler knows its event | | Broadcasting from UseCase | Broadcasting from @EventsHandler only | | Importing gateway in domain/application | Inject via WsGatewayPort TOKEN |


References

| File | Content | |------|---------| | references/gateway-patterns.md | Full gateway + Redis adapter + JWT handshake + rooms | | references/ws-gateway-port.md | Port interface + adapter + mock for testing | | references/frontend-consumption.md | Client Socket.IO + reconnection + React hooks |

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.