A full stack task manager with user accounts, secure login, and a real PostgreSQL database. People register, log in, and manage their own tasks: create, edit, tick off, delete, and filter by status. Built as a portfolio project to practice SQL, authentication, and REST API design.
Live demo: fullstack-task-manager-v5bk.onrender.com (free hosting, so the first load can take up to a minute)
- Register and log in with email and password
- Passwords are hashed with bcrypt, never stored as plain text
- JWT-based sessions (the login lasts 7 days)
- Private tasks: every user only sees and edits their own
- Create, edit, delete, and mark tasks as done
- Tasks are grouped by status (To-do, In progress, Done) with live counts
- Priority pills (low, medium, high) and due dates with an overdue marker
- Filter tasks by status from the sidebar
- Mobile friendly layout, with light and dark mode that follows your device
- Friendly error messages and a "waking up the server" notice for free hosting
- Backend: Node.js, Express 5
- Database: PostgreSQL on Neon (free tier), using the
pgdriver - Auth:
bcryptjsfor password hashing,jsonwebtokenfor JWT - Security extras:
express-rate-limitto slow down brute-force attempts - Frontend: plain HTML, CSS, and JavaScript (no framework)
- Hosting: Render (free web service)
All task routes need the header Authorization: Bearer <token>.
| Method | Route | What it does |
|---|---|---|
| POST | /api/auth/register |
Create an account |
| POST | /api/auth/login |
Log in and get a token |
| GET | /api/me |
Get the logged-in user |
| GET | /api/tasks |
List my tasks |
| POST | /api/tasks |
Create a task |
| PATCH | /api/tasks/:id |
Update a task (send only the fields to change) |
| DELETE | /api/tasks/:id |
Delete a task |
| GET | /api/health |
Server health check |
| GET | /api/health/db |
Database health check |
Two tables, defined in schema.sql:
users: id, name, unique email, password hash, created datetasks: id, owner (user_id), title, description, status, priority, due date, timestamps. Deleting a user also deletes their tasks, and status and priority only accept fixed values.
You need Node.js 20 or newer and a free Neon account.
-
Clone the repo and install packages:
git clone https://github.com/KelshRyleMaranan/fullstack-task-manager.git cd fullstack-task-manager npm install -
Create a free Neon project and copy its connection string.
-
Create a
.envfile in the project root (see.env.example):PORT=3000 DATABASE_URL=your-neon-connection-string JWT_SECRET=a-long-random-stringYou can generate a random secret with:
node -e "console.log(require('crypto').randomBytes(48).toString('hex'))" -
Create the tables:
npm run db:setup
-
Start the app:
npm run dev
Then open http://localhost:3000.
fullstack-task-manager/
├── server.js # Express app, rate limits, error handling
├── db.js # PostgreSQL connection pool
├── setup-db.js # Creates the tables from schema.sql
├── schema.sql # users and tasks tables
├── routes/
│ ├── auth.js # register and login
│ └── tasks.js # task CRUD, scoped to the logged-in user
├── middleware/
│ └── auth.js # checks the JWT on protected routes
└── public/ # frontend (HTML, CSS, JS)
- Passwords are hashed with bcrypt before they reach the database
- All SQL uses parameterized queries, which prevents SQL injection
- Every task query is filtered by the logged-in user's id
- Login and sign-up routes are rate limited
- Task text is rendered with
textContent, so it can't inject HTML - Secrets live in a
.envfile that is never committed
The token is kept in localStorage to keep the project simple. A production app might use httpOnly cookies instead.
The app runs on free plans, so the first visit after a quiet period can take up to a minute while the server wakes up. The database also sleeps when idle and wakes on the next request.
Built by Kelsh Ryle Maranan
- GitHub: @KelshRyleMaranan
- LinkedIn: Maranan Kelsh Ryle
