Architecture: How should native WebSocket (/ws) and Socket.IO (/command-ce...
Documentation for Sessions
🏗️ Architecture: How should native WebSocket (/ws) and Socket.IO (/command-ce...
Date: 2026-02-06
Time: 21:02:58 UTC
Session: editor2-97.8
Agent: backend
Confidence: 100%
Status: ✅ Documented
📋 Decision Context
Question:
How should native WebSocket (/ws) and Socket.IO (/command-center, /mcp-stream) coexist on the same HTTP server port?
Task: WebSocket upgrade routing architecture for backend-ai-api
🎯 Decision Made
Chosen:
noServer:true + prependListener to intercept /ws before engine.io
Options Considered:
- Separate HTTP servers on different ports
- noServer:true + prependListener to intercept /ws before engine.io
- Custom IoAdapter with server caching
- Use server.on('upgrade') with explicit path routing
💭 Reasoning
NestJS IoAdapter creates a SEPARATE Socket.IO/engine.io instance per @WebSocketGateway (no server caching in createIOServer). Each engine.io attach() adds its own 'upgrade' listener. With destroyUpgrade:true (default), engine.io schedules socket.destroy() after 1s for upgrade requests it doesn't recognize. The solution: (1) NativeWsGateway uses new WebSocketServer({ noServer: true }) so ws doesn't add its own catch-all upgrade handler. (2) Uses server.prependListener('upgrade', ...) so the /ws handler fires BEFORE engine.io handlers. (3) For /ws requests, calls wss.handleUpgrade() which consumes the socket. (4) Setting destroyUpgrade:false on Socket.IO gateways provides belt-and-suspenders safety — engine.io won't destroy sockets claimed by native WS. (5) attachToServer() is called from main.ts AFTER app.listen() to guarantee all Socket.IO gateways are fully wired first.
📁 Files Involved
apis/backend-ai-api/src/app/mcp-gateway/native-ws.gateway.tsapis/backend-ai-api/src/main.tsapis/backend-ai-api/src/app/command-center/command-center.gateway.tsapis/backend-ai-api/src/app/mcp-gateway/mcp-gateway.gateway.ts
🔗 Related
- Decision ID:
dev_1770411778095_20jouuc - Session: 97
- Workspace: editor2
Auto-generated by MCP Smart Consolidation