# Websocket Broadcasting

> 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.

- **Type:** Skill
- **Install:** `agentstack add skill-softtor-nestjs-hexagonal-websocket-broadcasting`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [Softtor](https://agentstack.voostack.com/s/softtor)
- **Installs:** 0
- **Category:** [Databases](https://agentstack.voostack.com/c/databases)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [Softtor](https://github.com/Softtor)
- **Source:** https://github.com/Softtor/nestjs-hexagonal/tree/main/skills/websocket-broadcasting
- **Website:** https://github.com/Softtor/nestjs-hexagonal#readme

## Install

```sh
agentstack add skill-softtor-nestjs-hexagonal-websocket-broadcasting
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## 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/`:

```typescript
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`:

```typescript
@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:

```typescript
@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:

```typescript
// 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.

- **Author:** [Softtor](https://github.com/Softtor)
- **Source:** [Softtor/nestjs-hexagonal](https://github.com/Softtor/nestjs-hexagonal)
- **License:** MIT
- **Homepage:** https://github.com/Softtor/nestjs-hexagonal#readme

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

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-softtor-nestjs-hexagonal-websocket-broadcasting
- Seller: https://agentstack.voostack.com/s/softtor
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
