Install
$ agentstack add skill-softtor-nestjs-hexagonal-websocket-broadcasting ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo 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.
Verified badge
Passed review? Show it. Paste this badge into your README — it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming — see below.
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 →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.
- Author: Softtor
- Source: Softtor/nestjs-hexagonal
- License: MIT
- Homepage: https://github.com/Softtor/nestjs-hexagonal#readme
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.