A Spring Boot REST API for personal finance management with JWT authentication, email verification, expense/income tracking, and financial analytics. Built with Spring Security, MySQL, and Flutter mobile frontend.
Note: This is a personal learning project built to understand full-stack development. While functional, it's designed for educational purposes and local use.
- Features
- Tech Stack
- Demo
- Project Structure
- Prerequisites
- Installation
- Configuration
- API Documentation
- Database Schema
- Security
- Known Limitations
- Future Improvements
- Contributing
- License
- 🔐 JWT-based stateless authentication
- 📧 Email verification with OTP during registration
- 🔑 BCrypt password hashing with salt
- 🚪 Secure login/logout flow
- ⏱️ Token expiration handling
- ➕ Create, read, update, and delete expenses
- 🏷️ Categorize expenses (Food, Transport, Shopping, etc.)
- 📅 Date-based expense tracking
- 💰 Amount tracking with decimal precision
- 💵 Record income transactions
- 📈 Track income sources with categories
- 📊 Income history management
- 📊 Monthly expense breakdown by category (Pie charts)
- 📉 Yearly spending trends (Line & Bar charts)
- 💹 Income vs Expense comparison
- 📅 Dashboard with latest transactions
- 🔢 Min/Max expense tracking
- 👤 View user profile information
- 📊 Access to all historical data
- 🎨 Dark/Light theme support (Flutter app)
- 📧 Welcome email on registration
- 🔐 OTP verification emails
- 📮 Brevo SMTP integration
- Spring Boot 3.x - Application framework
- Spring Security - Authentication & Authorization
- Spring Data JPA - Data persistence layer
- Hibernate - ORM framework
- MySQL 8.0 - Relational database
- HikariCP - Database connection pooling
- JWT (JSON Web Tokens) - Stateless authentication
- BCrypt - Password hashing
- Brevo SMTP - Email service
- Maven - Build & dependency management
- Flutter - Cross-platform mobile app (Android & iOS)
- Dart - Programming language
- Lombok - Reduce boilerplate code
- Jakarta Validation - Input validation
- Springdoc OpenAPI - API documentation (Swagger)
Coming Soon: Add screenshots of your Flutter app here
- Login/Registration screen
- Dashboard with charts
- Expense list
- Add expense form
- Dark/Light mode comparison
Coming Soon: Add a short demo video (30-60 seconds) showing the app flow
ExpenseTracker/
├── src/
│ ├── main/
│ │ ├── java/com/example/ExpenseTracker/
│ │ │ ├── Controller/ # REST API endpoints
│ │ │ │ ├── AuthController.java
│ │ │ │ ├── ExpenseController.java
│ │ │ │ ├── IncomeController.java
│ │ │ │ ├── ProfileController.java
│ │ │ │ └── StatsController.java
│ │ │ │
│ │ │ ├── DTO/ # Data Transfer Objects
│ │ │ │ ├── ExpenseDTO.java
│ │ │ │ ├── GraphDTO.java
│ │ │ │ ├── IncomeDTO.java
│ │ │ │ └── StatsDTO.java
│ │ │ │
│ │ │ ├── Entity/ # Database entities
│ │ │ │ ├── Expense.java
│ │ │ │ ├── Income.java
│ │ │ │ ├── UserEntity.java
│ │ │ │ └── PendingRegistration.java
│ │ │ │
│ │ │ ├── Filter/ # Security filters
│ │ │ │ └── JwtRequestFilter.java
│ │ │ │
│ │ │ ├── IO/ # Request/Response models
│ │ │ │ ├── AuthRequest.java
│ │ │ │ ├── AuthResponse.java
│ │ │ │ ├── ProfileRequest.java
│ │ │ │ └── ResetPasswordRequest.java
│ │ │ │
│ │ │ ├── Repository/ # Data access layer
│ │ │ │ ├── ExpenseRepository.java
│ │ │ │ ├── IncomeRepository.java
│ │ │ │ ├── UserRepository.java
│ │ │ │ └── PendingRegistrationRepository.java
│ │ │ │
│ │ │ ├── Service/ # Business logic
│ │ │ │ ├── Stats/
│ │ │ │ │ ├── StatsService.java
│ │ │ │ │ └── StatsServiceImpl.java
│ │ │ │ ├── AppUserDetailsService.java
│ │ │ │ ├── EmailService.java
│ │ │ │ ├── ExpenseService.java
│ │ │ │ ├── IncomeService.java
│ │ │ │ └── ProfileService.java
│ │ │ │
│ │ │ ├── SpringConfig/ # Configuration classes
│ │ │ │ ├── CustomAuthenticationEntryPoint.java
│ │ │ │ └── SecurityConfig.java
│ │ │ │
│ │ │ └── Util/ # Utility classes
│ │ │ └── JwtUtil.java
│ │ │
│ │ └── resources/
│ │ ├── application.properties # Production config
│ │ └── application-dev.properties # Development config
│ │
│ └── test/ # Unit tests (TODO)
│ └── java/com/example/ExpenseTracker/
│
├── .gitignore
├── pom.xml
└── README.md
Before running this application, ensure you have:
- Java 17 or higher - Download
- Maven 3.6+ - Download
- MySQL 8.0+ - Download
- Git - Download
- Brevo Account (for email service) - Sign up
# Backend
git clone https://github.com/seshathri044/expense-tracker-backend.git
cd expense-tracker-backend
# Frontend (in a separate terminal)
git clone https://github.com/seshathri044/expense-tracker-frontend.gitCREATE DATABASE expense_tracker;- Create a free account at Brevo
- Go to SMTP & API section
- Generate SMTP credentials
- Verify your sender email address
Create a .env file or set environment variables:
# Database
export DB_URL="jdbc:mysql://localhost:3306/expense_tracker?useSSL=false&serverTimezone=Asia/Kolkata"
export DB_USER="root"
export DB_PASSWORD="your_mysql_password"
# JWT Secret (generate a strong secret)
export JWT_SECRET_KEY="your_jwt_secret_key_here"
# Email Configuration
export MAIL_USERNAME="your_brevo_smtp_username"
export MAIL_PASSWORD="your_brevo_smtp_password"
export MAIL_FROM="your_verified_email@example.com"
# Server
export SERVER_PORT="8080"Generate a secure JWT secret:
openssl rand -base64 64For local development, you can directly update the file:
spring.datasource.url=jdbc:mysql://localhost:3306/expense_tracker
spring.datasource.username=root
spring.datasource.password=YOUR_PASSWORD
jwt.secret.key=YOUR_GENERATED_SECRET
spring.mail.username=YOUR_BREVO_USERNAME
spring.mail.password=YOUR_BREVO_PASSWORD
spring.mail.properties.mail.smtp.from=YOUR_EMAILmvn clean install# Development mode
mvn spring-boot:run -Dspring-boot.run.profiles=dev
# Or production mode
mvn spring-boot:run -Dspring-boot.run.profiles=prodThe backend will start on http://localhost:8080/api
cd expense-tracker-frontend
flutter pub get
flutter run- SQL logging enabled
- Detailed error messages
- Debug logging level
- Uses environment variables
- Minimal logging
- Production-ready settings
All API endpoints are prefixed with /api:
- Login:
http://localhost:8080/api/login - Expenses:
http://localhost:8080/api/expenses - Stats:
http://localhost:8080/api/stats/dashboard
HikariCP Connection Pool Settings:
- Maximum pool size: 10
- Minimum idle connections: 5
- Connection timeout: 30 seconds
- Idle timeout: 10 minutes
- Max lifetime: 30 minutes
http://localhost:8080/api
Access interactive API documentation:
http://localhost:8080/api/swagger-ui.html
POST /api/register
Content-Type: application/json
{
"email": "user@example.com",
"password": "SecurePass123!"
}Response:
{
"message": "OTP sent to your email. Please verify.",
"email": "user@example.com"
}POST /api/verify-otp
Content-Type: application/json
{
"email": "user@example.com",
"otp": "123456"
}Response:
{
"message": "Email verified successfully. You can now login."
}POST /api/login
Content-Type: application/json
{
"email": "user@example.com",
"password": "SecurePass123!"
}Response:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"email": "user@example.com"
}POST /api/logout
Authorization: Bearer <your-jwt-token>All endpoints below require authentication:
Authorization: Bearer <your-jwt-token>POST /api/expenses
Content-Type: application/json
{
"title": "Grocery Shopping",
"amount": 1500.00,
"date": "2024-12-31",
"category": "Food",
"description": "Weekly groceries"
}GET /api/expensesGET /api/expenses/{id}PUT /api/expenses/{id}
Content-Type: application/json
{
"title": "Updated Grocery",
"amount": 1600.00,
"date": "2024-12-31",
"category": "Food",
"description": "Updated description"
}DELETE /api/expenses/{id}POST /api/income
Content-Type: application/json
{
"title": "Monthly Salary",
"amount": 50000.00,
"date": "2024-12-01",
"category": "Salary",
"description": "December salary"
}GET /api/incomePUT /api/income/{id}DELETE /api/income/{id}GET /api/stats/dashboardResponse:
{
"totalIncome": 50000.00,
"totalExpense": 25000.00,
"balance": 25000.00,
"latestIncomes": [...],
"latestExpenses": [...],
"minExpense": 100.00,
"maxExpense": 5000.00
}GET /api/stats/chartReturns data for monthly expense breakdown by category.
GET /api/profilePOST /api/send-reset-otp
Content-Type: application/json
{
"email": "user@example.com"
}POST /api/reset-password
Content-Type: application/json
{
"email": "user@example.com",
"otp": "123456",
"newPassword": "NewSecurePass123!"
}CREATE TABLE user_entity (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
email VARCHAR(255) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
is_verified BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);CREATE TABLE pending_registration (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
email VARCHAR(255) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
otp VARCHAR(6) NOT NULL,
otp_created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);CREATE TABLE expense (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(255) NOT NULL,
amount DECIMAL(19,2) NOT NULL,
date DATE NOT NULL,
category VARCHAR(100),
description TEXT,
user_id BIGINT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES user_entity(id) ON DELETE CASCADE
);CREATE TABLE income (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(255) NOT NULL,
amount DECIMAL(19,2) NOT NULL,
date DATE NOT NULL,
category VARCHAR(100),
description TEXT,
user_id BIGINT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES user_entity(id) ON DELETE CASCADE
);-
Registration:
- User submits email and password
- System generates OTP and sends to email
- User data stored in
pending_registrationtable
-
OTP Verification:
- User enters OTP received via email
- System validates OTP (expires after 10 minutes)
- User moved to
user_entitytable with verified status
-
Login:
- User submits credentials
- System validates email verification status
- BCrypt compares hashed passwords
- JWT token generated and returned
-
Protected Routes:
- Client includes JWT in Authorization header
JwtRequestFilterintercepts and validates token- User identity extracted from token
- Request proceeds if valid
- Hashing Algorithm: BCrypt with automatic salt generation
- Password Requirements:
- Minimum 8 characters (recommended)
- Mix of letters, numbers, symbols (recommended by users)
- Storage: Only hashed passwords stored in database
- Reset: Secure OTP-based password reset flow
- Algorithm: HS256 (HMAC with SHA-256)
- Token Expiration: Configurable (default: 24 hours recommended)
- Claims: Contains user email and issued time
- Secret: Stored in environment variables (never committed to repo)
Currently configured to allow all origins for development:
configuration.setAllowedOriginPatterns(List.of("*"));configuration.setAllowedOrigins(List.of(
"https://your-frontend-domain.com"
));- ❌ No unit tests implemented yet
- ❌ No integration tests
- ❌ No test coverage reports
⚠️ No pagination implemented (will have performance issues with large datasets)⚠️ No filtering/sorting options for expenses and income lists⚠️ No rate limiting (vulnerable to abuse)⚠️ No API versioning
⚠️ Hard delete only (no soft delete for recovery)⚠️ No data export (CSV, PDF, Excel)⚠️ No data backup functionality⚠️ No audit trail (who deleted what and when)
⚠️ No recurring expense tracking⚠️ No budget alerts/notifications⚠️ No multi-currency support⚠️ No receipt/document upload⚠️ No sharing expenses with family/friends⚠️ No categories management (predefined only)
⚠️ CORS allows all origins (needs production configuration)⚠️ No refresh token mechanism⚠️ No device tracking⚠️ No suspicious login alerts
⚠️ Not deployed to cloud⚠️ No Docker containerization⚠️ No CI/CD pipeline⚠️ No monitoring/logging infrastructure
- Add comprehensive unit tests (JUnit 5, Mockito)
- Implement pagination for all list endpoints
- Add proper API documentation with Swagger annotations
- Implement data export features (CSV, PDF)
- Add soft delete with recovery option
- Deploy to cloud (AWS/Heroku/Railway)
- Add refresh token mechanism
- Implement rate limiting (bucket4j/resilience4j)
- Add budget tracking and alerts
- Implement recurring expenses
- Add audit logging
- Create Docker containers
- Set up CI/CD with GitHub Actions
- Multi-currency support
- Receipt upload functionality
- Expense sharing with other users
- Custom categories management
- Mobile push notifications
- Data visualization enhancements
- Export to accounting software
# Run all tests
mvn test
# Run with coverage
mvn clean test jacoco:report
# View coverage report
open target/site/jacoco/index.htmlCurrent Status: ❌ Tests not yet implemented
| Status | Description | When It Occurs |
|---|---|---|
| 200 | OK | Successful request |
| 201 | Created | Resource created successfully |
| 400 | Bad Request | Invalid input data |
| 401 | Unauthorized | Missing or invalid JWT token |
| 403 | Forbidden | Valid token but insufficient permissions |
| 404 | Not Found | Resource doesn't exist |
| 409 | Conflict | Email already exists |
| 500 | Internal Server Error | Unexpected server error |
{
"timestamp": "2024-12-31T10:15:30",
"status": 400,
"error": "Bad Request",
"message": "Invalid email format",
"path": "/api/register"
}- ✅ HikariCP Connection Pooling - Efficient database connection management
- ✅ Stateless JWT - No server-side session storage
- ✅ Lazy Loading - Hibernate lazy loading for relationships
- ✅ Indexed Columns - User email and foreign keys indexed
⚠️ No caching implemented (Redis/Caffeine)⚠️ No pagination (memory issues with large datasets)⚠️ No database query optimization analysis⚠️ No CDN for static assets
Already covered in Installation section.
# Dockerfile (to be created)
FROM openjdk:17-jdk-slim
WORKDIR /app
COPY target/expense-tracker-*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]Not yet deployed, but compatible with:
- AWS EC2 / Elastic Beanstalk
- Heroku
- Railway
- Google Cloud Run
- Azure App Service
Contributions are welcome! This is a learning project, so feel free to:
- Report bugs
- Suggest new features
- Submit pull requests
- Improve documentation
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Write clear commit messages
- Add comments for complex logic
- Update README if adding new features
- Test your changes locally before PR
This project is open source and available under the MIT License.
Seshathri M
- GitHub: @seshathri044
- LinkedIn: Seshathri M
- Email: mseshathri507@gmail.com
Frontend Repository: expense-tracker-frontend
- Spring Boot and Spring Security communities
- Flutter framework and Dart team
- MySQL documentation
- Brevo for email service
- Stack Overflow community
- YouTube tutorials and blog posts that helped during development
If you encounter any issues or have questions:
- Check the Known Limitations section
- Open an issue in the repository with detailed information
- Email: mseshathri507@gmail.com
If you're learning from this project, here are helpful resources:
- Spring Boot Documentation
- Spring Security with JWT
- Flutter Documentation
- MySQL Tutorial
- REST API Best Practices