A full-stack to-do list application with real JWT authentication, a MongoDB-backed REST API, and a Dockerized development/production setup.
- User registration and login with JWT (
jsonwebtoken+bcryptjspassword hashing). - Create, list, edit, complete, delete, and drag-and-drop reorder tasks — reordering
is persisted on the server (
PATCH /todos/reorder), not just visual. - Optional due dates per task.
- Data persistence with MongoDB.
- Responsive interface with TailwindCSS + shadcn/ui components.
- Backend health check endpoint (
GET /health) and DockerHEALTHCHECK. - Automated CI (GitHub Actions): backend lint + tests, frontend lint + typecheck + build, and a Docker image build check.
- Frontend: React 19, Vite, TypeScript, TailwindCSS, shadcn/ui, @dnd-kit.
- Backend: Node.js, Express, Mongoose, JWT, bcryptjs.
- Database: MongoDB.
- Infrastructure: Docker, Docker Compose.
- DevOps: GitHub Actions (CI).
- Testing: Jest + Supertest (backend), with Mongoose models mocked so the suite runs deterministically without a live database connection.
backend/
src/
config/ # env loading + Mongo connection
models/ # Mongoose schemas (User, Todo)
middlewares/ # auth guard, centralized error handler
controllers/ # route handlers
routes/ # Express routers (auth, todos, health)
app.js # Express app factory (used by both server and tests)
index.js # process entrypoint (validates env, connects DB, starts server)
tests/
unit/ # pure validation logic
integration/ # HTTP-level tests via supertest, with mocked Mongoose models
frontend/
src/ # React app (Vite + TypeScript)
docker-compose.yml # db, api, web, tests services
.github/workflows/ci.yml
You need Docker and Docker Compose. No local Node or MongoDB install required.
-
Copy the environment template and set a real JWT secret:
cp .env.example .env # Edit .env and replace JWT_SECRET with the output of: openssl rand -base64 48 -
Start the stack:
docker-compose up --build
-
Access the application:
- Frontend: http://localhost:8080
- Backend API: http://localhost:5000
- Backend health check: http://localhost:5000/health
Requires Node.js 20+ and a running MongoDB instance.
# Backend
cd backend
cp .env.example .env # set JWT_SECRET and MONGO_URI
npm install
npm run dev # http://localhost:5000
# Frontend (separate terminal)
cd frontend
npm install
npm run dev # http://localhost:8080Backend tests are unit/integration tests written with Jest and Supertest. The integration tests exercise the real Express routes and middleware, but the Mongoose models are mocked with deterministic fixtures — they do not require a running MongoDB instance and do not hit any external service.
# Locally
cd backend
npm test
# Via Docker Compose
docker-compose run --rm testsThere is currently no automated frontend test suite. Frontend correctness is
covered by npm run lint, npm run typecheck, and npm run build in CI.
| Command | Description |
|---|---|
npm run dev |
Start with nodemon (auto-restart) |
npm start |
Start once, no auto-restart |
npm run lint |
ESLint |
npm test |
Jest test suite |
npm run test:coverage |
Jest with coverage report |
| Command | Description |
|---|---|
npm run dev |
Vite dev server |
npm run build |
Production build |
npm run lint |
ESLint |
npm run typecheck |
tsc --noEmit |
| Method | Route | Auth | Description |
|---|---|---|---|
| POST | /auth/register |
No | Create a user account |
| POST | /auth/login |
No | Log in and receive a JWT |
| GET | /auth/me |
Yes | Return the authenticated user |
| GET | /todos |
Yes | List the user's tasks |
| POST | /todos |
Yes | Create a task |
| PUT | /todos/:id |
Yes | Toggle completed |
| PATCH | /todos/:id |
Yes | Edit text and/or dueDate |
| PATCH | /todos/reorder |
Yes | Persist a new task order ({ orderedIds: string[] }) |
| DELETE | /todos/:id |
Yes | Delete a task |
| GET | /health |
No | Liveness/readiness check |
All /todos routes are scoped to the authenticated user's own tasks.
- No password-reset flow.
- No rate limiting on
/auth/loginor/auth/register(recommended before any public deployment). - No end-to-end/browser test suite — only backend unit/integration tests exist.
- The production frontend bundle is a single ~260 KB gzip chunk; code-splitting was intentionally left out of scope for this pass.
JWT_SECRETmust be provided via environment variable; the application refuses to start without it outside of the test environment.- If you obtained this repository from a version where a real secret value was
committed to
docker-compose.ymlorbackend/src/index.js, treat that value as compromised and rotate/replace it — do not reuse it.