A real-time collaborative survey editing system built with Survey Creator by SurveyJS. Multiple users can edit the same survey or form simultaneously, similar to Google Docs for document editing.
- Frontend – React + SurveyJS Creator (
survey-core,survey-creator-core,survey-creator-react) - Backend – three interchangeable WebSocket servers behind one shared frontend — Node.js (
packages/server-nodejs/), Go (packages/server-go/), .NET (packages/server-net/) - Storage – In-memory sessions (MVP, no persistence or auth)
- Users join or create a session via a session ID.
- Each session stores a Survey Creator model on the server in memory.
- Every edit in the creator is captured as a serialized change message.
- Changes are sent to the server over WebSocket and broadcast to other clients.
- Receiving clients apply updates to their local Survey Creator instance.
- Temporary suppression of local change listeners prevents update loops.
- Ordering is server-driven (last-write-wins at message level).
The repo ships three interchangeable servers that speak the same wire protocol, so the single packages/frontend/ client works against any of them (it derives the WebSocket URL from the page origin):
packages/server-nodejs/— the reference server. Holds a headless SurveyJS model per session and feeds late joiners a computed schema + undo/redo stack.packages/server-go/(Go) andpackages/server-net/(.NET) — thin relays with no SurveyJS dependency. They store the session's seed schema and an ordered log of messages; a late joiner receives the seed plus a replay of the log and reconstructs an equivalent state and undo/redo stack locally. See docs/relay-server-architecture.md.
Build the client once (npm run build:client), then launch any server on http://localhost:8080:
| Command | Server |
|---|---|
npm run dev |
Node.js (live Vite HMR, no prebuild needed) |
npm run go |
Go relay (serves the built dist/client) |
npm run net |
.NET relay (requires the .NET 8 SDK; serves the built dist/client) |
Each serves the same client + /api + /ws on one port. Per-server details are in each folder's README.md.
- The Node server serves the API and WebSocket endpoint (raw
http+ws, no framework). - In development, the client is served via Vite middleware on the same port.
- In production, static assets are served from the built client bundle.
- The client listens to
creator.undoRedoController.undoRedoManager.onSerializedChanges. - Each change (property update, array mutation, undo, redo) is serialized into an
ISyncMessageand sent to the server. - The server applies the message to the session's in-memory
SurveyModel, ensuring new clients receive the latest state. - The server then broadcasts the message to all other clients in the same session.
- Each client applies incoming messages via
manager.applySerialized(message). - During application, local change listeners are temporarily disabled to prevent echoing remote updates back to the server.
npm install
npm run devThe application is available at http://localhost:8080. The server hosts the React client through embedded Vite middleware on the same port.
Open the app to create a session, then copy the invite link to collaborate in another tab.
A session ID can also be supplied directly in the URL path (/<sessionId>)—the client will join that session instead of creating a new one.
| Variable | Default | Description |
|---|---|---|
PORT |
8080 |
HTTP + WebSocket port |
NODE_ENV |
development |
Enables production mode when set to production (disables Vite middleware) |
CLIENT_DIST |
dist/client |
Directory containing the production client build |
EMPTY_SESSION_TTL_MS |
1800000 (30 min) |
Time before an empty session is garbage-collected |
npm run build
npm startnpm run build compiles the server and builds the client application. npm start serves the production build on http://localhost:8080.
In production mode the server serves the static client bundle from dist/client (override with CLIENT_DIST) and does not load Vite.
| Method | Path | Description |
|---|---|---|
| POST | /api/sessions |
Create a session |
| GET | /api/sessions/:id |
Fetch session state |
| GET | /health |
Health check |
| WS | /ws/sessions/:id |
Collaboration channel |
One shared frontend, three interchangeable servers:
packages/
frontend/ Shared React + SurveyJS Creator client (works with any server)
server-nodejs/ Node.js reference server (runs SurveyJS server-side)
server-go/ Go relay server (no SurveyJS)
server-net/ .NET relay server (no SurveyJS)
docs/ Architecture notes
e2e/ Playwright tests
packages/frontend/CollaborativeCreator.tsx— Survey Creator embedding and session handlingpackages/frontend/collab-client.ts— Client-side sync logicpackages/server-nodejs/index.ts— HTTP + WebSocket serverpackages/server-nodejs/session-store.ts— In-memory session state and sync applicationpackages/server-go/andpackages/server-net/— relay servers (see each folder'sREADME.md)
The shared wire-protocol types (ISyncMessage, IClientToServer, IServerToClient, …) are imported from survey-creator-core.
- In-memory sessions only
- No authentication
- No persistence
- No presence or cursor tracking