A production-ready, real-time collaborative drawing application inspired by Excalidraw. This project demonstrates end-to-end system design including frontend, backend APIs, WebSockets, database management, containerization, and cloud deployment.
- Next.js (React Framework)
- TypeScript
- Tailwind CSS
- Deployed on Vercel
- Node.js + Express
- WebSocket (ws library)
- Prisma ORM
- PostgreSQL Database
- Deployed on Render (Docker)
- Turborepo (Monorepo Management)
- pnpm Workspaces
- Docker
- GitHub Actions (CI/CD)
callab-draw/
├── apps/
│ ├── web/ # Next.js frontend application
│ ├── http-backend/ # Express REST API server
│ └── ws-server/ # WebSocket server for real-time collaboration
│
├── packages/
│ ├── db/ # Prisma schema and database utilities
│ ├── backend-common/ # Shared backend utilities and types
│ ├── ui/ # Shared UI components library
│ └── typescript-config/ # Shared TypeScript configurations
│
├── turbo.json # Turborepo pipeline configuration
├── package.json # Root package.json with workspace scripts
├── pnpm-workspace.yaml # pnpm workspace configuration
└── README.md
- Real-time Collaboration: Multiple users can draw simultaneously on the same canvas
- Multi-user Rooms: Create and join collaborative drawing sessions
- JWT Authentication: Secure user authentication and authorization
- WebSocket Communication: Low-latency real-time updates
- Data Persistence: PostgreSQL database with Prisma ORM
- Automatic Migrations: Database schema migrations on deployment
- Dockerized Services: Containerized backend for consistent deployments
- Monorepo Architecture: Shared code and streamlined development
Before you begin, ensure you have the following installed:
- Node.js: >= 18.x
- pnpm: >= 8.x
- Docker: (optional, for local database)
- PostgreSQL: >= 14.x (if not using Docker)
Create a .env file in the root directory with the following variables:
# Database
DATABASE_URL=postgresql://user:password@localhost:5432/excalidraw
# Authentication
JWT_SECRET=your_jwt_secret_key
NEXTAUTH_SECRET=your_nextauth_secret_key
# API Endpoints
NEXT_PUBLIC_API_URL=http://localhost:3001
NEXT_PUBLIC_SOCKET_URL=ws://localhost:4000
NEXT_PUBLIC_SITE_URL=http://localhost:3000
# Environment
NODE_ENV=developmentNote: Production environment variables are configured separately in Render and Vercel dashboards.
git clone https://github.com/your-username/collabdraw.git
cd collabdrawpnpm installdocker run -d \
--name excalidraw-db \
-p 5432:5432 \
-e POSTGRES_USER=user \
-e POSTGRES_PASSWORD=password \
-e POSTGRES_DB=excalidraw \
postgres:15Install PostgreSQL locally and create a database:
createdb excalidrawpnpm db:migratepnpm devThis starts:
- Frontend: http://localhost:3000
- Backend API: http://localhost:3001
- WebSocket Server: ws://localhost:4000
# Frontend only
pnpm dev:web
# Backend API only
pnpm dev:api
# WebSocket server only
pnpm dev:wspnpm buildThis builds all applications in the monorepo using Turborepo's caching.
docker build -f apps/http-backend/Dockerfile -t excalidraw-backend .docker run -d \
-p 3001:3001 \
-e DATABASE_URL=your_database_url \
-e JWT_SECRET=your_secret \
excalidraw-backend- Create a new Web Service on Render
- Connect your GitHub repository
- Select Docker as the environment
- Set the Dockerfile path:
apps/http-backend/Dockerfile - Add environment variables from your
.envfile - Enable Auto-Deploy from the main branch
- Deploy
- Import your GitHub repository on Vercel
- Set the Root Directory to
apps/web - Framework Preset: Next.js
- Add environment variables:
NEXT_PUBLIC_API_URL=https://your-backend.onrender.com NEXT_PUBLIC_SOCKET_URL=wss://your-ws.onrender.com NEXTAUTH_SECRET=your_secret - Deploy
Migrations run automatically on application startup. To run manually:
pnpm db:migrateTo create a new migration:
pnpm db:migrate:dev --name your_migration_nameconst token = "your_jwt_token";
const ws = new WebSocket(`wss://your-ws-server.onrender.com?token=${token}`);
ws.onopen = () => {
console.log("Connected to WebSocket server");
// Join a room
ws.send(
JSON.stringify({
type: "JOIN_ROOM",
roomId: "room_123",
}),
);
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log("Received:", data);
};
ws.onerror = (error) => {
console.error("WebSocket error:", error);
};JOIN_ROOM: Join a collaborative drawing roomDRAW: Send drawing data to other usersCURSOR_MOVE: Update cursor positionELEMENT_UPDATE: Update canvas elements
Currently using manual testing. Future improvements:
# Unit tests (planned)
pnpm test
# E2E tests (planned)
pnpm test:e2e- GitHub Integration: Auto-deploy on push to main branch
- Render Pipeline: Automatic Docker builds and deployments
- Vercel Pipeline: Automatic frontend builds and deployments
- Prisma Migrations: Run automatically on backend startup
- Stateless Services: Horizontal scaling capability
- Connection Pooling: Efficient database connections via Prisma
- CDN Distribution: Static assets served via Vercel's Edge Network
- Turborepo Caching: Faster builds with intelligent caching
- WebSocket Optimization: Binary protocol for reduced bandwidth
- JWT Authentication: Secure token-based authentication
- Environment Variables: Sensitive data stored securely
- CORS Configuration: Restricted API access
- WSS Protocol: Encrypted WebSocket connections
- Input Validation: Server-side validation for all inputs
- Redis pub/sub for multi-server WebSocket scaling
- Role-Based Access Control (RBAC)
- Offline mode with conflict resolution
- Canvas version history and rollback
- Mobile-responsive drawing interface
- Export to PNG/SVG/PDF
- Integration tests with Jest
- E2E tests with Playwright
- Performance monitoring and analytics
Contributions are welcome! Please follow these steps:
- Fork the repository
- Create a feature branch:
git checkout -b feature/your-feature - Commit your changes:
git commit -m 'Add some feature' - Push to the branch:
git push origin feature/your-feature - Open a Pull Request
This project is licensed under the MIT License.
Rohit Kumar
- GitHub: @rohitdev_sol
- Inspired by Excalidraw
- Built for learning and production experimentation
- Special thanks to the open-source community
If you find this project helpful, please consider giving it a star on GitHub!