Collaborative Document Engine
Build the backend engine of a collaborative document editor in TypeScript, like the core of Notion: documents are made of blocks (headings, paragraphs, lists and to-dos), and several people change them at once through operations such as insert block, update text, move and delete. The Node.js server validates every operation with Zod, orders them with version numbers, transforms operations that were made on an older version so they still apply correctly, broadcasts them to everyone over WebSockets, and saves an operation log plus regular snapshots, so documents can be recovered exactly after a crash. A small React client shows it working. You'll learn operation-based collaboration, operational transformation for block edits, validation at a trust boundary, durable logs and snapshots, and testing convergence.
- Estimate
- ~15.5h
- Steps
- 7
- Completed by
- 0
- Proposed by
- codeseed.app
Node.js · React · Zod
Project roadmap
- 01
Design the operations
~2.5hWrite DESIGN.md: a document is an ordered list of blocks (id, type and content), and operations are insertBlock (after a block id), deleteBlock, moveBlock, updateBlock (replacing text, or inserting and deleting characters at positions) and setType. Each operation has a client id, a sequence number and the document version it was based on. Explain how the server orders operations and how an operation based on an older version is transformed against the ones that came after it, with examples of conflicting edits. Done when: DESIGN.md covers every operation and conflict case.
- 02
Validate operations with Zod
~1.5hWrite Zod schemas for every operation as a discriminated union, with limits (block content length, number of blocks and valid block types), and derive the TypeScript types. Write a pure apply(document, operation) function that rejects operations referring to blocks that don't exist. Done when: tests show valid operations applied and invalid ones rejected with clear errors.
- 03
Transform concurrent operations
~3hWrite transform(a, b) that adjusts operation a so it still makes sense after b was applied: character positions shift after inserts and deletes before them, updates to a deleted block are dropped, and moves and inserts relative to a deleted block fall back to a sensible position. The server transforms each incoming operation against every operation since its base version before applying it. Done when: tests with pairs of conflicting operations give the same final document in either order.
- 04
Broadcast over WebSockets
~2hBuild a Node.js server with the ws package: clients join a document and receive its current state and version; each operation is validated, transformed, applied, given the next version, acknowledged to its sender and broadcast to the others. Process each document's operations one at a time (with a queue per document) so ordering is strict. Ignore repeated operations by client id and sequence number. Done when: several clients editing the same document stay in sync.
- 05
Persist operations and snapshots
~2hAppend every accepted operation to a log (in SQLite or PostgreSQL) before broadcasting it, and save a snapshot of the document every N operations. On startup, or when a document is opened, load the latest snapshot and replay the operations after it. Let clients that reconnect ask for the operations after their last version. Done when: restarting the server loses no accepted operation, and clients catch up after reconnecting.
- 06
Build a small React client
~2hBuild a React client that renders blocks, lets you add, edit, move and delete them, sends operations optimistically, transforms its pending operations against incoming ones (the same logic as the server), and shows who's connected. Done when: two browser windows can edit the same document at once and always end up identical.
- 07
Test convergence
~2.5hWrite a randomized test that simulates several clients making random operations with random delays and reconnections against the real server, and checks that all clients and the stored document end up identical. Add tests for snapshots and recovery, limits, and invalid operations. Done when: the randomized test passes over thousands of runs.
Ready to build this?
Get a GitHub repo and start building. Your AI reviewer checks each step as you go.
Tech stack