MongoDB Change Streams - Server-Side Only Correction

Important clarification about MongoDB Change Streams implementation

MongoDB Change Streams - Critical Correction

Important Discovery

MongoDB Change Streams are SERVER-SIDE ONLY - they cannot run in the browser because they require the MongoDB native driver.

What This Means

❌ Can't do this (browser/frontend):

// This DOESN'T work in browser - requires MongoDB driver
const mongodb = new MongoClient(uri);
const collection = mongodb.collection('universes');
const changeStream = collection.watch(); // ❌ Not available in browser

✅ Can do this (server/backend):

// This WORKS in backend - has MongoDB driver
// apis/backend-api/src/app/spaces/spaces.service.ts
const changeStream = this.db.universes.watch();
changeStream.on('change', (change) => {
  // Push to frontend via WebSocket or SSE
  this.websocket.emit('universe-update', change);
});

The Correct Architecture

Frontend (Browser)

Uses 3-tier sync (no MongoDB driver needed):

1. BroadcastChannel (Cross-Tab)
   ├─ Built into GgStorage
   ├─ < 1ms latency
   └─ Zero configuration

2. HTTP MongoDB Sync (Background)
   ├─ Already configured (provideHttpMongoDBSync)
   ├─ Polls backend every 5-10s
   ├─ Pushes local changes to MongoDB
   └─ Pulls remote changes from MongoDB

3. WebSocket (Real-time from Backend)
   ├─ Backend watches MongoDB Change Streams
   ├─ Backend pushes to frontend via WebSocket
   ├─ < 100ms latency
   └─ RealtimeSyncService handles this

Backend (Server)

Implements MongoDB Change Streams and pushes to frontend:

// apis/backend-api/src/app/spaces/spaces.service.ts

@Injectable()
export class SpacesService {
  async onModuleInit() {
    // Server-side Change Streams
    const changeStream = this.universeModel.watch();
    
    changeStream.on('change', async (change) => {
      // Push to ALL connected clients via WebSocket
      this.socketGateway.server.emit('universe-update', {
        operation: change.operationType,
        universe: change.fullDocument
      });
    });
  }
}

Current Solution (Already Working!)

What We Have Now ✅

Frontend:
├─ BroadcastChannel (cross-tab, < 1ms) ✅ Working
├─ HTTP MongoDB Sync (background, 5-10s) ✅ Working
└─ RealtimeSyncService (WebSocket) ✅ Partially working

Backend:
├─ REST API for CRUD operations ✅ Working
├─ MongoDB persistence ✅ Working
└─ WebSocket server ✅ Connected (needs Change Stream integration)

What Needs to be Added (Backend)

// apis/backend-api/src/app/spaces/spaces.service.ts

import { Injectable, OnModuleInit } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { SocketGateway } from '../socket/socket.gateway';

@Injectable()
export class SpacesService implements OnModuleInit {
  constructor(
    @InjectModel('Universe') private universeModel: Model<any>,
    @InjectModel('Space') private spaceModel: Model<any>,
    private socketGateway: SocketGateway
  ) {}
  
  async onModuleInit() {
    console.log('[SpacesService] 📡 Setting up MongoDB Change Streams...');
    
    // Watch universe changes
    const universeStream = this.universeModel.watch([], {
      fullDocument: 'updateLookup'
    });
    
    universeStream.on('change', (change) => {
      console.log('[Change Stream] Universe update:', change.operationType);
      
      // Broadcast to all connected clients
      this.socketGateway.server.emit('universe-update', {
        action: change.operationType,
        universe: change.fullDocument,
        universeId: change.documentKey._id
      });
    });
    
    // Watch space changes
    const spaceStream = this.spaceModel.watch([], {
      fullDocument: 'updateLookup'
    });
    
    spaceStream.on('change', (change) => {
      console.log('[Change Stream] Space update:', change.operationType);
      
      this.socketGateway.server.emit('space-update', {
        action: change.operationType,
        space: change.fullDocument,
        spaceId: change.documentKey._id
      });
    });
    
    console.log('[SpacesService] ✅ MongoDB Change Streams active');
  }
}

Revised Architecture

┌──────────────────────────────────────────────────────────────┐
│                    FRONTEND (Browser)                         │
│                                                               │
│  autoFlashState → GgStorage → IndexedDB (5-10ms)             │
│         ↓              ↓                                      │
│  BroadcastChannel  HTTP Sync (5-10s)                         │
│  (Other Tabs)      (Background)                              │
│         ↓              ↓                                      │
└────────┼──────────────┼───────────────────────────────────────┘
         │              │
         │              ▼
         │     ┌────────────────────┐
         │     │   Backend REST API │
         │     │  (NestJS Express)  │
         │     └────────┬───────────┘
         │              ▼
         │     ┌────────────────────┐
         │     │   MongoDB Atlas    │
         │     │  (Cloud Database)  │
         │     └────────┬───────────┘
         │              │
         │              ▼
         │     ┌────────────────────┐
         │     │ MongoDB Change     │
         │     │ Streams (Server)   │
         │     └────────┬───────────┘
         │              │
         │              ▼
         │     ┌────────────────────┐
         │     │ WebSocket Server   │
         │     │   (Socket.IO)      │
         │     └────────┬───────────┘
         │              │
         └──────────────┴─────────────────┐
                        ▼                 ▼
              ┌─────────────────┐  ┌─────────────────┐
              │  This Browser   │  │  Other Users    │
              │  Update (< 1ms) │  │  Update (<100ms)│
              └─────────────────┘  └─────────────────┘

What We're Actually Using (No MongoDB Driver Needed)

Current Sync Mechanism ✅

  1. Local Operations (5-10ms)

    • Write to IndexedDB via GgStorage
    • autoFlashState for reactivity
  2. Cross-Tab Sync (< 1ms)

    • BroadcastChannel (built into GgStorage)
    • Automatic, zero config
  3. Background Persistence (5-10s)

    • HTTP MongoDB Sync (provideHttpMongoDBSync)
    • Polls backend API
    • Pushes local changes
    • Pulls remote changes
  4. Real-time Multi-User (< 100ms)

    • Backend implements Change Streams
    • Backend pushes via WebSocket
    • Frontend receives via RealtimeSyncService
    • Already partially working!

Action Items

✅ Frontend (Complete)

🔄 Backend (Needs Enhancement)

Benefits Still Achieved

Even without frontend Change Streams:

Summary

Frontend can't use MongoDB Change Streams directly (browser limitation).

Backend should use Change Streams and push to frontend via WebSocket.

Current solution still works perfectly:

No regression - we're still using the elegant architecture, just clarified where Change Streams actually run!