A full-stack MERN task management app. Quiet, opinionated, built for individuals who want to capture what they need to do and check it off without fighting their tools.
Stack: React (Vite), Node.js, Express, MongoDB, JWT auth, Tailwind CSS.
- Email and password auth with JWT tokens. Bcrypt-hashed passwords, 7-day expiry, per-user data isolation.
- Full CRUD on tasks. Title, description, status (todo / doing / done), priority (low / medium / high), due date.
- Status workflow. Click the status pill on any task to advance it through the workflow.
- Filters and search. Filter by status and priority, full-text search across title and description, all server-side.
- Live stats bar. Total, by status, plus an overdue count that flags tasks past their due date.
- Editorial design. Fraunces display font, DM Sans body, JetBrains Mono accents. Warm cream background, ink text, amber accents. No purple gradients on white.
taskpilot/
server/ Node.js + Express API
config/db.js Mongoose connection
models/ User, Task schemas
middleware/ JWT auth, error handler
controllers/ Auth and task logic
routes/ Auth and task endpoints
server.js Entry point
client/ React + Vite app
src/
api/axios.js Axios instance with JWT interceptor
context/ Auth context (login, register, logout, user)
components/ Navbar, StatsBar, TaskCard, TaskFormModal, etc.
pages/ Login, Register, Dashboard
App.jsx Router setup
main.jsx Entry point
package.json Top-level scripts (dev, install:all, build)
- Node.js 18 or higher
- npm 9 or higher
- MongoDB (one of the following):
- Local install: https://www.mongodb.com/try/download/community
- Cloud: a free MongoDB Atlas cluster at https://www.mongodb.com/atlas
From the project root:
npm run install:allThis installs the root, server, and client packages in one go.
Copy the example file and fill it in:
cp server/.env.example server/.envOpen server/.env and set:
MONGO_URIto your MongoDB connection string. For local:mongodb://localhost:27017/taskpilot. For Atlas: paste the connection string from the Atlas UI.JWT_SECRETto any long random string. You can generate one with:node -e "console.log(require('crypto').randomBytes(48).toString('hex'))"CLIENT_URLis set tohttp://localhost:5173by default for the Vite dev server. Change it if you move the client.
From the root:
npm run devThis boots the API on http://localhost:5000 and the React client on http://localhost:5173. The Vite dev server proxies /api/* to the backend so you don't need CORS in development.
Open http://localhost:5173 in your browser, register an account, and start adding tasks.
From the project root:
| Script | Effect |
|---|---|
npm run dev |
Run server and client concurrently with hot reload |
npm run server |
Run only the backend (nodemon) |
npm run client |
Run only the frontend (Vite) |
npm run build |
Build the React client for production |
npm run install:all |
Install dependencies for root, server, and client |
npm start |
Run the production server (after build) |
All task endpoints require a JWT in the Authorization: Bearer <token> header. Tokens are returned on register and login.
| Method | Path | Body | Returns |
|---|---|---|---|
| POST | /api/auth/register |
{ name, email, password } |
{ user, token } |
| POST | /api/auth/login |
{ email, password } |
{ user, token } |
| GET | /api/auth/me |
(none) | { user } |
| Method | Path | Body / Query | Returns |
|---|---|---|---|
| GET | /api/tasks |
?status=&priority=&q= |
{ tasks: [...] } |
| GET | /api/tasks/stats |
(none) | { total, todo, doing, done, overdue } |
| POST | /api/tasks |
{ title, description?, status?, priority?, dueDate? } |
{ task } |
| PATCH | /api/tasks/:id |
any task fields | { task } |
| DELETE | /api/tasks/:id |
(none) | { message, id } |
GET /api/health returns { status: "ok", service: "taskpilot-api", time }.
| Field | Type | Notes |
|---|---|---|
| name | String | Required, max 60 |
| String | Required, unique, lowercase, validated | |
| password | String | Required, min 8, bcrypt-hashed, never returned |
| createdAt / updatedAt | Date | Auto |
| Field | Type | Notes |
|---|---|---|
| user | ObjectId | Reference to User, indexed |
| title | String | Required, max 140 |
| description | String | Optional, max 2000 |
| status | enum | "todo", "doing", or "done" (default todo) |
| priority | enum | "low", "medium", or "high" (default medium) |
| dueDate | Date | Optional |
| completedAt | Date | Auto-set when status flips to done |
| createdAt / updatedAt | Date | Auto |
The aesthetic is intentionally not the generic SaaS template. Colour palette is warm and editorial:
#1a1614ink (text, dark surfaces)#fbf8f3cream (background)#f7f3edbone (secondary surfaces)#d97706amber (primary accent)#9a3412rust (errors, overdue)#4d7c5esage (success, done)
Typography pairs Fraunces (serif display, italic emphasis) with DM Sans (body) and JetBrains Mono (microtype, eyebrows, stats). The login and register pages use a 60/40 split layout with a dark marketing panel on the left and a clean form on the right.
Any Node.js host works (Render, Railway, Fly.io, Heroku). Set the environment variables from server/.env in the host's dashboard.
npm run buildOutputs to client/dist/. Deploy to Vercel, Netlify, Cloudflare Pages, or any static host. Update the CLIENT_URL in your backend .env to match the deployed URL, and update the baseURL in client/src/api/axios.js (or use a Vite env var) to point at your deployed API.
MongoDB Atlas free tier handles development and small projects. Whitelist your backend's IP and paste the connection string into MONGO_URI.
MIT. See LICENSE for details.
Built by Mulualem Kahssay. May 2026.