expense-teacker

πŸ’° Expense Tracker - Full Stack Web Application

A beginner-friendly full stack web application for tracking personal expenses. Built with React, Node.js, Express, and MongoDB.

✨ Features

πŸ›  Tech Stack

Layer Technology
Frontend React, React Router, Axios, CSS
Backend Node.js, Express.js
Database MongoDB + Mongoose
Auth JWT + bcrypt

πŸ“ Folder Structure

expense-tracker/
β”œβ”€β”€ backend/                  # Express API server
β”‚   β”œβ”€β”€ controllers/          # Route handler logic
β”‚   β”œβ”€β”€ models/               # MongoDB schemas
β”‚   β”œβ”€β”€ routes/               # API route definitions
β”‚   β”œβ”€β”€ middleware/            # Auth middleware
β”‚   β”œβ”€β”€ server.js             # Entry point
β”‚   β”œβ”€β”€ .env                  # Environment variables
β”‚   └── package.json
β”œβ”€β”€ frontend/                 # React app (Vite)
β”‚   β”œβ”€β”€ src/
β”‚   β”‚   β”œβ”€β”€ components/       # Navbar, ProtectedRoute
β”‚   β”‚   β”œβ”€β”€ pages/            # Login, Register, Dashboard, AddExpense, EditExpense
β”‚   β”‚   β”œβ”€β”€ App.jsx           # Main app with routing
β”‚   β”‚   β”œβ”€β”€ main.jsx          # React entry point
β”‚   β”‚   └── index.css         # All styles
β”‚   β”œβ”€β”€ index.html
β”‚   β”œβ”€β”€ vite.config.js
β”‚   └── package.json
β”œβ”€β”€ .github/workflows/ci.yml  # GitHub Actions CI
β”œβ”€β”€ .gitignore
└── README.md

πŸš€ Installation

Prerequisites

1. Clone the repository

git clone https://github.com/your-username/expense-tracker.git
cd expense-tracker

2. Setup Backend

cd backend
npm install

Create a .env file in the backend/ folder:

PORT=5000
MONGO_URI=mongodb+srv://<username>:<password>@cluster0.xxxxx.mongodb.net/expense-tracker
JWT_SECRET=your_super_secret_key_change_this

3. Setup Frontend

cd frontend
npm install

4. Run the Application

Start backend (from backend/ folder):

npm run dev

Start frontend (from frontend/ folder):

npm run dev

Open http://localhost:5173 in your browser.

πŸ“‘ API Endpoints

Authentication

Method Endpoint Description Auth Required
POST /api/auth/register Create new account No
POST /api/auth/login Login to account No
GET /api/auth/user Get current user Yes

Expenses (All require JWT token in Authorization header)

Method Endpoint Description
GET /api/expenses Get all expenses
GET /api/expenses/:id Get single expense
POST /api/expenses Create expense
PUT /api/expenses/:id Update expense
DELETE /api/expenses/:id Delete expense

πŸ” Authentication Flow

  1. User registers or logs in β†’ Backend returns a JWT token
  2. Token is stored in localStorage on the browser
  3. Every protected API request includes Authorization: Bearer <token> header
  4. Backend verifies the token before processing the request
  5. When user logs out, the token is removed from localStorage

πŸ“Έ Screenshots

(Add screenshots here after running the app)

🌐 Deployment

Backend on Render

  1. Push code to GitHub
  2. Go to render.com β†’ New Web Service
  3. Connect your GitHub repo
  4. Set:
    • Root Directory: backend
    • Build Command: npm install
    • Start Command: npm start
  5. Add environment variables (MONGO_URI, JWT_SECRET)
  6. Deploy

Frontend on Vercel

  1. Go to vercel.com β†’ New Project
  2. Import your GitHub repo
  3. Set:
    • Root Directory: frontend
    • Build Command: npm run build
    • Output Directory: dist
  4. Add environment variable: VITE_API_URL = your Render URL
  5. Deploy

Database on MongoDB Atlas

  1. Go to mongodb.com/atlas
  2. Create free cluster
  3. Click β€œConnect” β†’ β€œConnect your application”
  4. Copy the connection string
  5. Replace <username> and <password> in your .env file

πŸ§ͺ Testing with Postman

  1. Register: POST http://localhost:5000/api/auth/register with JSON body { "name": "Test", "email": "test@test.com", "password": "123456" }
  2. Login: POST http://localhost:5000/api/auth/login with { "email": "test@test.com", "password": "123456" }
  3. Copy the token from the response
  4. Create Expense: POST http://localhost:5000/api/expenses with Authorization: Bearer <token> and body { "title": "Lunch", "amount": 15, "category": "Food" }
  5. Get Expenses: GET http://localhost:5000/api/expenses with Authorization: Bearer <token>

πŸ”„ CI/CD Pipeline

Three GitHub Actions workflows in .github/workflows/ run automatically:

Workflow Trigger Action
ci.yml Every push / PR to main Install + syntax-check backend, build frontend
vercel-deploy.yml Push to main (frontend changes) Build & deploy frontend to Vercel (--prod)
render-deploy.yml Push to main (backend changes) POST to the Render deploy hook

Required GitHub Secrets

Add these under repo β†’ Settings β†’ Secrets and variables β†’ Actions:

Secret How to get it
VERCEL_TOKEN vercel.com/account/tokens β†’ Create Token
VERCEL_ORG_ID Run npx vercel link in frontend/, then copy orgId from .vercel/project.json
VERCEL_PROJECT_ID Same projectId from .vercel/project.json
RENDER_DEPLOY_HOOK_URL Render β†’ Service β†’ Settings β†’ Deploy Hooks β†’ Create Hook (URL like https://api.render.com/deploy/srv-xxx?key=yyy)

Environment Variables

Live URLs

⚠️ Data persistence: the backend stores data in backend/data/*.json. Render free instances have an ephemeral filesystem, so data is lost whenever the service restarts or redeploys. For a persistent database, migrate the models to MongoDB Atlas (the MONGO_URI env var) β€” see the README architecture diagram.

πŸ“š Viva Questions & Answers

Q1: What is JWT and why is it used? A: JWT (JSON Web Token) is a compact, URL-safe token used for authentication. It contains encoded user information and is signed with a secret key. We use it so users don’t have to send their password on every request.

Q2: How does bcrypt work? A: bcrypt is a password hashing function. It adds a random β€œsalt” to the password before hashing, making it resistant to rainbow table attacks. We store only the hashed version, never the plain text password.

Q3: What is middleware in Express? A: Middleware are functions that execute during the request-response cycle. Our authMiddleware runs before protected route handlers to verify the JWT token.

Q4: What is the difference between SQL and NoSQL? A: SQL databases (like MySQL) use tables with fixed schemas. NoSQL databases (like MongoDB) use flexible documents. MongoDB is easier for beginners because you can store JSON-like data without predefined schemas.

Q5: How does React Router work? A: React Router is a library that enables navigation between different components without page refresh. It uses Routes and Route components to map URLs to components.

Q6: What is CORS? A: CORS (Cross-Origin Resource Sharing) is a security mechanism that allows a web page from one domain to access resources on another domain. We enable it in Express so our React frontend can call the backend API.

Q7: Explain the flow when a user logs in. A: User enters email/password β†’ Frontend sends POST to /api/auth/login β†’ Backend finds user, compares hashed password β†’ If match, generates JWT token β†’ Frontend stores token in localStorage β†’ User is redirected to Dashboard.

Q8: What is the purpose of the .env file? A: The .env file stores sensitive configuration like database URLs and secret keys. It keeps secrets out of the codebase and is listed in .gitignore so it’s never committed to GitHub.

πŸ— Architecture Diagram

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                      Browser (React)                        β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚  Login    β”‚  β”‚ Register β”‚  β”‚  Dashboard / Add / Edit   β”‚  β”‚
β”‚  β”‚  Page     β”‚  β”‚  Page    β”‚  β”‚  (Protected Routes)       β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚        β”‚              β”‚                  β”‚                  β”‚
β”‚        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                  β”‚
β”‚                          β”‚   axios (HTTP requests)          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                           β”‚
                    β”Œβ”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”
                    β”‚   CORS       β”‚
                    β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
                           β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    Express Server                            β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚   Auth   β”‚   β”‚  Auth Middlewareβ”‚   β”‚   Expense Routes β”‚  β”‚
β”‚  β”‚  Routes  │──▢│ (JWT Verify)   │──▢│   (CRUD)         β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚       β”‚                                       β”‚            β”‚
β”‚  β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”                          β”Œβ”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚  Auth    β”‚                          β”‚   Expense    β”‚   β”‚
β”‚  β”‚Controllerβ”‚                          β”‚  Controller  β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜                          β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚       β”‚                                       β”‚            β”‚
β”‚  β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”                          β”Œβ”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚  User    β”‚                          β”‚   Expense    β”‚   β”‚
β”‚  β”‚  Model   β”‚                          β”‚   Model      β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜                          β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚       β”‚                                       β”‚            β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
        β”‚                                       β”‚
        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                        β”‚
                 β”Œβ”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”
                 β”‚   MongoDB   β”‚
                 β”‚   (Atlas)   β”‚
                 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ’‘ Future Improvements

πŸ“„ License

This project is for educational purposes as a college mini project.