Skip to content

WebSocket Architecture Plan

Related Documentation:

Overview

Real-time collaboration for audio production workflow. Users need instant updates for:

  • Comments on tracks (the primary use case)
  • Notification delivery
  • Collaborator activity (online status, role changes)
  • File updates (new versions, playback sync)

Technology Choice: Socket.io

OptionProsConsVerdict
Socket.ioBattle-tested, auto-reconnect, rooms, NestJS gateway support, fallbacksSlightly larger bundleRecommended
PusherManaged service, simpleMonthly costs, external dependency
Native WebSocketSmallest bundleManual reconnection, no rooms

Why Socket.io: NestJS has built-in @nestjs/websockets with Socket.io adapter. Handles reconnections, namespaces, and rooms natively.


Real-Time Event Types

1. Comment Events (Wave View)

EventTriggerPayloadRecipients
comment:newNew comment created{commentId, content, owner, timeStart}All track viewers
comment:replyReply added{parentId, replyId, content, owner}All track viewers
comment:likeComment liked{commentId, likes, userId}Comment owner
comment:resolvedComment resolved{commentId, resolvedBy}All track viewers
comment:deletedComment deleted{commentId}All track viewers

2. Notification Events

EventTriggerPayloadRecipients
notification:newAny notification{id, type, message, slug}Recipient only
notification:countUnread count change{count}User only

3. Collaboration Events

EventTriggerPayloadRecipients
collaborator:invitedNew invite{email, role, resource}Resource owner
collaborator:joinedAccept invite{userId, name, role}All collaborators
collaborator:role-changedRole update{userId, oldRole, newRole}Affected user + admins
collaborator:removedUser removed{userId}All collaborators
collaborator:onlinePresence update{userId, isOnline}Resource viewers

4. File/Track Events

EventTriggerPayloadRecipients
file:uploadedNew version{fileId, version, name}Track collaborators
track:updatedTrack metadata changed{trackId, changes}Track collaborators
playback:syncPlayhead position{trackId, position, userId}Track viewers

Room Strategy

Namespace: /
├── Room: user:{userId}           # Personal notifications
├── Room: track:{trackId}         # Comments, playback sync
├── Room: project:{projectId}     # File updates, collaborator changes
└── Room: artist:{artistId}       # High-level activity

Join Logic:

  • User connects → auto-join user:{userId}
  • Navigate to Wave view → join track:{trackId}
  • Navigate to Project → join project:{projectId}

Backend Integration Points

Files to Modify:

  1. New Module: src/modules/websocket/websocket.gateway.ts
  2. Inject Gateway: Comment, Notification, Collaboration, File services
  3. Frontend: src/lib/socket-provider.tsx

Implementation Phases

Phase 1: Foundation (Priority: High)

  • [ ] Install Socket.io: npm install @nestjs/websockets socket.io
  • [ ] Create WebSocket gateway with JWT auth
  • [ ] Add room join/leave handlers
  • [ ] Create frontend socket provider

Phase 2: Comments Real-Time (Priority: High)

  • [ ] Emit events from CommentService
  • [ ] Update Wave view to subscribe to track room
  • [ ] Show new comments without refresh
  • [ ] Live likes/resolved updates

Phase 3: Notifications (Priority: Medium)

  • [ ] Push notifications to user room
  • [ ] Update notification bell count instantly
  • [ ] Toast for high-priority notifications

Phase 4: Collaboration (Priority: Medium)

  • [ ] Collaborator joined/left events
  • [ ] Online presence indicator
  • [ ] Role change notifications

Phase 5: File Activity (Priority: Low)

  • [ ] New file version alerts
  • [ ] Playback sync (advanced)

Frontend Integration

tsx
// src/lib/socket-provider.tsx
'use client';

import { io, Socket } from 'socket.io-client';
import { useSession } from 'next-auth/react';
import { createContext, useContext, useEffect, useState } from 'react';

export const SocketContext = createContext<Socket | null>(null);

export function SocketProvider({ children }) {
  const { data: session } = useSession();
  const [socket, setSocket] = useState<Socket | null>(null);

  useEffect(() => {
    if (session?.accessToken) {
      const newSocket = io(process.env.NEXT_PUBLIC_API_BASE_URL, {
        auth: { token: session.accessToken },
      });
      setSocket(newSocket);
      return () => { newSocket.close(); };
    }
  }, [session]);

  return (
    <SocketContext.Provider value={socket}>
      {children}
    </SocketContext.Provider>
  );
}

User Review Required

IMPORTANT

Decision Needed: Playback sync feature

  • Option A: Simple - Users manually sync playhead
  • Option B: Real-time - Broadcast playhead position to all viewers (complex, potentially noisy)

NOTE

Scalability: For production with 1000+ concurrent users, consider Redis adapter for Socket.io to share state across multiple backend instances.


Verification Plan

Phase 1 Tests:

  • [ ] WebSocket connects with valid JWT
  • [ ] Invalid JWT rejected
  • [ ] Rooms join/leave correctly

Phase 2 Tests:

  • [ ] Create comment → appears in other browser
  • [ ] Like comment → owner sees update
  • [ ] Resolve comment → all viewers see status change

Ctrl-Audio Platform Documentation