Skip to content

RaveenaBhasin/collab-editor

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Collaborative Editor

A real-time collaborative text editor built with Rust and React. This project started as a learning exercise to master async Rust, WebSocket communication, concurrent state management, and service architecture patterns.

What It Does

Multiple users can connect to the same document and edit it together in real-time. Changes from one user are instantly broadcast to all other connected users via WebSocket. The backend manages document state, handles concurrent edits, and provides a REST API for document management.

Tech Stack

Backend (Rust)

The backend is built with modern async Rust, focusing on high concurrency and performance:

  • Tokio - The async runtime that powers everything. Handles thousands of concurrent connections efficiently.
  • Axum - A modern, type-safe web framework built on top of Tower. Used for HTTP endpoints and WebSocket handling.
  • Tower - Middleware ecosystem for request/response processing (logging, CORS, compression).
  • DashMap - A concurrent HashMap that's faster than RwLock<HashMap> because it shards data internally, allowing parallel access to different keys.
  • parking_lot - A faster alternative to std's RwLock with better performance for uncontended locks.
  • tokio-util - Provides CancellationToken for graceful shutdown of services.
  • async-trait - Enables async functions in traits (needed for the Service trait pattern).
  • serde / serde_json - Serialization framework for JSON message handling.
  • uuid - Generates unique IDs for documents and clients.
  • tracing / tracing-subscriber - Structured logging for observability.
  • anyhow / thiserror - Error handling libraries.

Frontend (React + TypeScript)

A clean React application that connects to the backend:

  • React 19 - The UI framework.
  • TypeScript - Type safety for the frontend code.
  • Vite - Fast build tool and dev server.
  • WebSocket API - Native browser WebSocket for real-time communication.

Project Evolution

This project evolved through several versions, each introducing new concepts:

Version 0.1: Async Foundations

Started with a simple TCP echo server to learn Tokio fundamentals - async/await, task spawning, and Arc for shared ownership.

Version 0.2: WebSocket Communication

Added Axum web framework and WebSocket support. Implemented broadcast channels so messages from one client reach all others. Learned about socket splitting (separating read/write halves) and using tokio::select! to handle concurrent operations.

Version 0.3: Shared State & Multi-Threading

Introduced multi-document support with concurrent access. Used DashMap for storing documents (better than RwLock for concurrent access). Implemented interior mutability pattern with RwLock for document content. Added REST API endpoints for document CRUD operations. Learned about avoiding deadlocks and safely handling locks in async code.

Version 0.4: Service Architecture

Refactored to a service-based architecture with lifecycle management. Created a Service trait that all services implement, allowing uniform management. Built a ServiceMonitor that coordinates starting and stopping services. Implemented graceful shutdown using CancellationToken - when you press Ctrl+C, all services finish their current work cleanly before shutting down.

Three services run concurrently:

  • WebSocketService - Runs the Axum HTTP/WebSocket server
  • AutoSaveService - Periodically saves documents every 30 seconds
  • PresenceService - Tracks which users are active in each document

Running the Project

Backend

# Build and run
cargo run

# The server starts on http://127.0.0.1:8080

The server provides:

  • REST API at /documents for document management
  • WebSocket endpoint at /documents/{id}/ws for real-time editing
  • Health check at /health

Frontend

cd frontend
npm install
npm run dev

The frontend will be available at http://localhost:5173 (or similar, Vite will tell you).

Testing

You can test the WebSocket connection using websocat:

# Install websocat
cargo install websocat

# Connect to a document
websocat ws://127.0.0.1:8080/documents/{document-id}/ws

Or use the React frontend to create documents and edit them in real-time.

API Endpoints

  • GET /health - Health check
  • GET /documents - List all documents
  • POST /documents - Create a new document (body: {"title": "My Document"})
  • GET /documents/{id} - Get document content
  • DELETE /documents/{id} - Delete a document
  • WS /documents/{id}/ws - WebSocket connection for real-time editing

WebSocket Message Format

Client → Server:

{"type": "Insert", "position": 0, "text": "Hello"}
{"type": "Delete", "position": 5, "length": 3}
{"type": "Sync"}

Server → Client:

{"type": "Sync", "content": "Hello world", "version": 1}
{"type": "Operation", "op_type": "Insert", "position": 0, "text": "Hi", "client_id": "...", "version": 2}

Project Structure

collab-editor/
├── src/
│   ├── main.rs              # Application entry point, service orchestration
│   ├── monitor.rs           # ServiceMonitor for lifecycle management
│   ├── handlers/
│   │   ├── http.rs          # REST API handlers
│   │   └── websocket.rs     # WebSocket handlers
│   ├── models/
│   │   ├── document.rs      # Document data structure
│   │   └── operation.rs     # Edit operation types
│   ├── services/
│   │   ├── service_trait.rs # Service trait definition
│   │   ├── websocket.rs     # WebSocket service
│   │   ├── autosave.rs      # Auto-save background service
│   │   └── presence.rs      # User presence tracking service
│   └── state/
│       └── document_store.rs # Document storage with DashMap
├── frontend/
│   └── src/
│       ├── App.tsx          # Main React component
│       ├── components/       # React components (Editor, DocumentList, etc.)
│       ├── hooks/
│       │   └── useWebSocket.ts # WebSocket connection hook
│       └── api.ts           # REST API client
└── docs/                    # Detailed documentation for each version

What I Learned

This project was a deep dive into:

  1. Async Rust - Understanding how Tokio schedules tasks, when to use .await vs spawn, and how to share state safely across async tasks.

  2. Web Frameworks - Axum's extractor pattern provides type-safe dependency injection. No runtime errors from missing parameters - the compiler catches them.

  3. Concurrency - The difference between Mutex and RwLock, when to use DashMap, how to avoid deadlocks, and why you shouldn't hold locks across .await points.

  4. Service Architecture - Building a microservice-style system with proper lifecycle management. Services can be started, monitored, and shut down gracefully.

  5. Real-Time Systems - WebSocket protocol, broadcast patterns, and handling concurrent edits without conflicts.

About

A real-time collaborative text editor built with Rust

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published