Days
Hours
Minutes
Seconds
x
Skip to content
Froala Documentation

Node.JS Collaboration Overview

The Node.js SDK includes an optional backend for the editor's real-time collaboration plugin. It is split into four modules you compose as needed — a WebSocket relay for live peer sync, plus REST + SQLite stores for suggestions & comments, version snapshots, and offline saves. All four can share a single HTTP server and a single SQLite file.

Module Purpose
Collaborative Pure WebSocket relay. Groups clients by docId and broadcasts messages between peers.
CollabPersistence REST + SQLite for track-change suggestions and inline comments.
VersionControl REST + SQLite for named or automatic content snapshots.
AsyncSave REST + SQLite for saving/loading the latest content in offline (async) mode.

Prerequisites

  • Node.js 18 or later.
  • Express — the CollabPersistence, VersionControl, and AsyncSave modules register their routes on an Express app.
  • better-sqlite3 — required by the three persistence modules. It ships as a regular dependency of the SDK and installs automatically with npm install; no separate step is needed.
  • Client side — the editor's collaboration plugin manages all CRDT/sync state with Yjs and connects over a standard WebSocket. The server is a transparent relay and never parses message content.

The three persistence modules can share one SQLite file (default collab.db), so a full setup needs just one HTTP server and one database.

Quick start

A single Express-wrapped HTTP server hosts the WebSocket relay and all three REST stores, backed by one shared SQLite file.

const http    = require('http');
const express = require('express');
const path    = require('path');
const {
  Collaborative,
  CollabPersistence,
  VersionControl,
  AsyncSave,
} = require('wysiwyg-editor-node-sdk');

const app = express();
app.use(express.json());

// One SQLite file shared by all three persistence modules
const dbPath = path.join(__dirname, 'data', 'collab.db');
CollabPersistence.attachRoutes(app, { dbPath });
VersionControl.attachRoutes(app, { dbPath });
AsyncSave.attachRoutes(app, { dbPath });

// Optional: expose live relay stats
app.get('/health', (req, res) => res.json(Collaborative.getStats()));

// Share ONE HTTP server between REST and the WebSocket relay
const server = http.createServer(app);
Collaborative.attachToServer(server);

server.listen(3000, () => {
  console.log('Collaborative backend on http://localhost:3000');
});

Use Collaborative.attachToServer(server) whenever the REST routes and the relay must share a port. Use Collaborative.createServer({ port }) only when the relay is the sole service on a port.

URL conventions

WebSocket : ws://<host>/{docId}
REST base : http://<host>/collab/{docId}/{resource}

The {docId} used by the WebSocket connection must match the :docId in the REST routes. Document IDs are arbitrary strings; URL-encode any characters outside a-z 0-9 - _.

Connect the editor to the backend

Point the editor at the backend in the plugin config.

Real-time mode

set the WebSocket URL:

new FroalaEditor('#editor', {
  pluginsEnabled: ['collaboration'],
  realTimeConfig: {
    syncUrl: 'ws://localhost:3000/my-doc-2024', // ws://<host>/{docId}
  },
});

Async (offline) mode

omit syncUrl. The editor then persists through the AsyncSave endpoints instead of syncing live. The save plugin posts the content under the field name body (its default saveParam), which matches what POST /collab/:docId/content expects.

Suggestions, comments, and version history persist automatically through the REST modules once their routes are mounted.

For the complete endpoint and method reference, see the Collaborative Backend API Reference.

Do you think we can improve this article? Let us know.