A full-stack task management platform with JWT authentication, color-tagged categories, task assignment, and full-text search — built on the MERN stack and deployed serverless.
Live App • Features • Tech Stack • Quick Start • API Reference
- Overview
- Features
- Tech Stack
- Project Structure
- Quick Start
- Environment Variables
- API Reference
- Data Models
- Deployment
- Known Limitations
- License
Planora is a MERN-stack task manager built to keep a simple promise: log in, see what's on your plate, and get through it — with categories, priorities, and search to keep things organized as the list grows.
Why this project:
- ✅ Real authentication — JWT access + refresh token flow with bcrypt password hashing and automatic silent token refresh on the frontend, not a mock login
- ✅ Soft-delete safe — deleting a task marks it removed instead of destroying the record
- ✅ Task assignment — tasks can be owned by one user and assigned to another, with access checks on both sides
- ✅ Deployed serverless — backend and frontend both run on Vercel, connected to MongoDB Atlas, with the Express app adapted to a serverless entry point
- ✅ Custom design system — a distinct visual identity (teal/amber/sage palette, serif + monospace type pairing) rather than default component styling
- Register / login with JWT access + refresh tokens
- Access tokens auto-refresh transparently via an Axios interceptor — no forced re-login on expiry
- Protected routes on both frontend (React Router guards) and backend (middleware)
- Rate-limited auth endpoints to slow brute-force attempts
- Create, edit, and soft-delete tasks with title, description, priority, due date, and category
- Status workflow: To do → In progress → Done → Archived, with
completed_attracked automatically - Full-text search across title and description
- Filter by status, priority, or category
- Assign a task to another user; both the owner and assignee can view and update status
- Color-tagged categories for quick visual grouping
- Three default categories (Personal, Work, Urgent) auto-created on signup
- Create, rename, or delete custom categories (defaults are protected from deletion)
- At-a-glance counts of To do / In progress / Done
- "Coming up next" view of unfinished tasks sorted by due date
- Editable name and email
- Secure password change flow (current password required)
Frontend
- React 18 (Vite)
- React Router
- Tailwind CSS
- Axios (with token-refresh interceptor)
- lucide-react (icons)
Backend
- Node.js + Express
- MongoDB + Mongoose
- JWT (jsonwebtoken) — separate access/refresh secrets
- bcryptjs
- express-validator, express-rate-limit, helmet, cors
Infrastructure
- MongoDB Atlas (database)
- Vercel (frontend + backend hosting, backend as serverless functions)
planora-mern/
├── backend/
│ ├── api/
│ │ └── index.js # Vercel serverless entry point
│ ├── src/
│ │ ├── config/ # Database connection (with connection caching)
│ │ ├── controllers/ # Route handlers (auth, tasks, categories, users)
│ │ ├── middleware/ # JWT auth guard, centralized error handler
│ │ ├── models/ # Mongoose schemas (User, Task, Category, RefreshToken)
│ │ ├── routes/ # Express route definitions
│ │ └── utils/ # JWT helpers, validators
│ ├── app.js # Express app (exported for both local + serverless use)
│ ├── server.js # Local dev entry point (app.listen)
│ └── vercel.json
└── frontend/
├── src/
│ ├── api/ # Axios client with refresh-token interceptor
│ ├── components/ # Layout, TaskCard, StatusPill, PriorityPill, ProtectedRoute
│ ├── context/ # Auth context
│ └── pages/ # Login, Register, Dashboard, Tasks, Categories, Profile
└── vercel.json
- Node.js 18+
- A MongoDB connection string (local or MongoDB Atlas)
cd backend
npm install
cp .env.example .env # then fill in your values
npm run dev # http://localhost:5000cd frontend
npm install
cp .env.example .env # VITE_API_URL=http://localhost:5000/api/v1
npm run dev # http://localhost:5173Open http://localhost:5173, click Create an account, and start adding tasks — three default categories are created for you automatically.
backend/.env
PORT=5000
NODE_ENV=development
MONGO_URI=your_mongodb_connection_string
JWT_ACCESS_SECRET=your_access_token_secret
JWT_REFRESH_SECRET=your_refresh_token_secret
JWT_ACCESS_EXPIRES=15m
JWT_REFRESH_EXPIRES=7d
CLIENT_ORIGINS=http://localhost:5173
frontend/.env
VITE_API_URL=http://localhost:5000/api/v1
On Vercel,
CLIENT_ORIGINSmust exactly match the deployed frontend URL, andVITE_API_URLmust point at the deployed backend +/api/v1.
All routes are prefixed /api/v1.
| Method | Route | Access | Description |
|---|---|---|---|
| POST | /auth/register |
Public | Create an account (also creates default categories) |
| POST | /auth/login |
Public | Log in |
| POST | /auth/refresh |
Public | Exchange refresh token for a new access token |
| POST | /auth/logout |
Public | Revoke a refresh token |
| GET | /auth/me |
Auth | Current user |
| GET | /tasks |
Auth | List tasks (filter by status, priority, category_id; paginated) |
| GET | /tasks/search?q= |
Auth | Full-text search |
| POST | /tasks |
Auth | Create a task |
| GET | /tasks/:id |
Auth | Get a single task |
| PUT | /tasks/:id |
Auth (owner) | Update a task |
| PATCH | /tasks/:id/status |
Auth (owner/assignee) | Update task status |
| DELETE | /tasks/:id |
Auth (owner) | Soft-delete a task |
| POST | /tasks/:id/assign |
Auth (owner) | Assign a task to another user |
| GET | /categories |
Auth | List categories |
| POST | /categories |
Auth | Create a category |
| PUT | /categories/:id |
Auth | Update a category |
| DELETE | /categories/:id |
Auth | Delete a category (defaults protected) |
| GET | /users/me |
Auth | View profile |
| PUT | /users/me |
Auth | Update profile |
| PUT | /users/me/password |
Auth | Change password |
User — name, email, password (hashed), createdAt
Task — title, description, status (todo/in_progress/done/archived), priority (low/medium/high), due_date, completed_at, deleted_at, user, assigned_to, category
Category — name, color, is_default, user
RefreshToken — token, user, expires_at, revoked (auto-expiring via MongoDB TTL index)
This project is deployed as two separate Vercel projects sharing one MongoDB Atlas cluster:
- Frontend → Vercel (root directory:
frontend) - Backend → Vercel serverless functions (root directory:
backend, entry pointapi/index.js)
See backend/vercel.json and frontend/vercel.json for the routing configuration. The Express app in backend/app.js is shared between the local dev server (server.js) and the serverless handler (api/index.js), with the MongoDB connection cached across warm serverless invocations.
- No seeded demo account yet — every visitor needs to register their own account to try the app
- No automated test suite
- Task assignment is single-assignee only; no multi-user collaboration on one task yet
- Category colors are chosen from a fixed swatch rather than a full color picker
This project is open for personal and educational use.