A real-time collaborative note-taking application where multiple users can edit the same document simultaneously and see each other's changes instantly.
- Create notes with a rich text editor (bold, italic, headings, lists)
- Share notes by sending the URL to collaborators
- Edit together in real-time — changes sync instantly across all viewers
- See who's online with presence indicators showing active users
- Secure access with user accounts (email/password or GitHub login)
| Component | Technology |
|---|---|
| Frontend | React, TypeScript, Vite, TipTap Editor |
| Backend | Node.js, Express, Socket.IO |
| Database | PostgreSQL (production), SQLite (development) |
| ORM | Prisma |
| Auth | JWT, GitHub OAuth |
| Hosting | Vercel (frontend), Railway (backend) |
- Node.js 18 or higher
- npm
git clone https://github.com/Audatic07/collab-notes.git
cd collab-notescd backend
npm install
npm run db:generate # Generate Prisma client
npm run db:push # Create database tables
npm run dev # Start server on port 3001Open a new terminal:
cd frontend
npm install
npm run dev # Start app on port 5173Go to http://localhost:5173, create an account, and start making notes.
collab-notes/
├── backend/
│ ├── prisma/
│ │ └── schema.prisma # Database schema
│ └── src/
│ ├── index.ts # Express server entry point
│ ├── routes/
│ │ ├── auth.ts # Register, login, user info
│ │ ├── github-auth.ts # GitHub OAuth flow
│ │ └── notes.ts # CRUD for notes
│ ├── socket/
│ │ └── index.ts # WebSocket event handlers
│ ├── middleware/
│ │ ├── auth.ts # JWT verification
│ │ └── errorHandler.ts # Error handling
│ └── lib/
│ ├── auth.ts # Password hashing, JWT utils
│ ├── env.ts # Environment config
│ ├── errors.ts # Custom error classes
│ └── prisma.ts # Database client
│
└── frontend/
└── src/
├── App.tsx # Routes
├── main.tsx # Entry point
├── index.css # Styles
├── pages/
│ ├── LoginPage.tsx
│ ├── RegisterPage.tsx
│ ├── AuthCallbackPage.tsx
│ ├── NotesListPage.tsx
│ └── NoteEditorPage.tsx
├── components/
│ ├── PresenceIndicator.tsx
│ └── ProtectedRoute.tsx
├── context/
│ └── AuthContext.tsx # Auth state management
└── lib/
├── api.ts # REST API client
└── socket.ts # WebSocket client
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/auth/register |
Create new account |
| POST | /api/auth/login |
Login, returns JWT |
| GET | /api/auth/me |
Get current user (requires auth) |
| GET | /api/auth/github |
Redirect to GitHub OAuth |
| GET | /api/auth/github/callback |
Handle OAuth callback |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/notes |
List all notes owned by user |
| POST | /api/notes |
Create a new note |
| GET | /api/notes/:id |
Get a specific note |
| PUT | /api/notes/:id |
Update note (owner only) |
| DELETE | /api/notes/:id |
Delete note (owner only) |
| Event | Direction | Purpose |
|---|---|---|
join-note |
Client → Server | Subscribe to updates for a note |
leave-note |
Client → Server | Unsubscribe from a note |
note-update |
Client → Server | Send content changes |
note-updated |
Server → Client | Receive content changes |
presence-update |
Server → Client | List of users viewing the note |
- Push code to GitHub
- Deploy backend to Railway (with PostgreSQL)
- Deploy frontend to Vercel
- Connect them with environment variables
git init
git add .
git commit -m "Initial commit"
git remote add origin https://github.com/YOUR_USERNAME/collab-notes.git
git branch -M main
git push -u origin main- Go to railway.app and sign in with GitHub
- Create new project → Deploy from GitHub repo → Select your repo
- Click the service → Settings → Set Root Directory to
backend - Add PostgreSQL: Click "New" → "Database" → "Add PostgreSQL"
- Add environment variables in the backend service:
JWT_SECRET= (any random string, 32+ characters)PORT=3001
- Set build command:
npm run db:use-postgres && npm run db:generate && npm run build - Set start command:
npm run db:push && npm start - Go to Settings → Networking → Generate Domain
- Copy your Railway URL (e.g.,
your-app.up.railway.app)
- Go to vercel.com and sign in with GitHub
- Add New → Project → Import your repo
- Set Root Directory to
frontend - Add environment variable:
VITE_API_URL=https://your-railway-url(from step 2)
- Deploy and copy your Vercel URL
Go back to Railway → backend service → Variables → Add:
FRONTEND_URL=https://your-vercel-url(from step 3)
Railway will auto-redeploy.
To enable "Login with GitHub":
- Go to github.com/settings/developers
- Create New OAuth App:
- Homepage URL: your Vercel URL
- Callback URL:
https://your-railway-url/api/auth/github/callback
- Add to Railway variables:
GITHUB_CLIENT_ID= (from GitHub)GITHUB_CLIENT_SECRET= (from GitHub)
The app uses a simple last-write-wins approach:
- User opens a note → joins a Socket.IO "room" for that note
- User types → changes are debounced (300ms) then sent to server
- Server saves to database and broadcasts to all users in the room
- Other users receive the update and their editors sync
This approach was chosen over more complex solutions (CRDTs, Operational Transform) because:
- The sync interval is fast enough that conflicts are rare
- The implementation is straightforward and maintainable
- It's similar to how Apple Notes and Google Keep handle sync
DATABASE_URL="file:./dev.db" # SQLite for local dev
JWT_SECRET="your-secret-key" # For signing JWTs
PORT=3001 # Server port
FRONTEND_URL="http://localhost:5173" # For CORS
GITHUB_CLIENT_ID="" # Optional: GitHub OAuth
GITHUB_CLIENT_SECRET="" # Optional: GitHub OAuthVITE_API_URL=http://localhost:3001 # Backend URLMIT