A production-ready Spring Boot REST API for personal finance management with JWT authentication, expense/income tracking, real-time analytics, and comprehensive CRUD operations. Built with Spring Security, MySQL, and modern best practices.
- Features
- Tech Stack
- Project Structure
- Prerequisites
- Installation
- Configuration
- API Documentation
- Database Schema
- Security
- Contributing
- License
- π JWT-based authentication
- π Secure password encryption
- π€ User registration and login
- π Token refresh mechanism
- π§ Email notifications
- π Role-based access control
- β Create, read, update, and delete expenses
- π·οΈ Categorize expenses
- π Track spending patterns
- π Filter and search expenses
- π Date-based expense tracking
- π΅ Record income transactions
- π Track income sources
- π Income analytics
- π Financial statistics
- π Spending trends
- πΉ Income vs Expense comparison
- π Monthly/Yearly reports
- π€ User profile management
- βοΈ Account settings
- π§ Email preferences
- Spring Boot 3.x - Application framework
- Spring Security - Authentication & Authorization
- Spring Data JPA - Data persistence
- Hibernate - ORM framework
- MySQL 8.0 - Primary database
- HikariCP - Connection pooling
- JWT (JSON Web Tokens) - Stateless authentication
- BCrypt - Password hashing
- Maven - Dependency management
- Lombok - Reduce boilerplate code
- ModelMapper - Object mapping
- Jakarta Validation - Input validation
ExpenseTracker/
βββ src/
β βββ main/
β β βββ java/com/example/ExpenseTracker/
β β β βββ Controller/
β β β β βββ AuthController.java
β β β β βββ ExpenseController.java
β β β β βββ IncomeController.java
β β β β βββ ProfileController.java
β β β β βββ StatsController.java
β β β β
β β β βββ DTO/
β β β β βββ ExpenseDTO.java
β β β β βββ GraphDTO.java
β β β β βββ IncomeDTO.java
β β β β βββ StatsDTO.java
β β β β
β β β βββ Entity/
β β β β βββ Expense.java
β β β β βββ Income.java
β β β β βββ UserEntity.java
β β β β
β β β βββ Filter/
β β β β βββ JwtRequestFilter.java
β β β β
β β β βββ IO/
β β β β βββ AuthRequest.java
β β β β βββ AuthResponse.java
β β β β βββ ProfileRequest.java
β β β β βββ ProfileResponse.java
β β β β βββ ResetPasswordRequest.java
β β β β
β β β βββ Repository/
β β β β βββ ExpenseRepository.java
β β β β βββ IncomeRepository.java
β β β β βββ UserRepository.java
β β β β
β β β βββ Service/
β β β β βββ Stats/
β β β β β βββ StatsService.java
β β β β β βββ StatsServiceImpl.java
β β β β βββ AppUserDetailsService.java
β β β β βββ EmailService.java
β β β β βββ ExpenseService.java
β β β β βββ ExpenseServiceImpl.java
β β β β βββ IncomeService.java
β β β β βββ IncomeServiceImpl.java
β β β β βββ ProfileService.java
β β β β βββ ProfileServiceImpl.java
β β β β
β β β βββ SpringConfig/
β β β β βββ CustomAuthenticationEntryPoint.java
β β β β βββ SecurityConfig.java
β β β β
β β β βββ Util/
β β β βββ JwtUtil.java
β β β
β β βββ resources/
β β βββ application.properties
β β βββ application-dev.properties
β β βββ application-prod.properties
β β βββ static/
β β βββ templates/
β β
β βββ test/
β βββ java/com/example/ExpenseTracker/
β
βββ .gitignore
βββ pom.xml
βββ README.md
Before running this application, ensure you have the following installed:
git clone https://github.com/yourusername/expense-tracker-backend.git
cd expense-tracker-backendCREATE DATABASE expense_tracker;Update src/main/resources/application.properties:
# Application Name
spring.application.name=ExpenseTracker
# Database Configuration
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/expense_tracker?useSSL=false&serverTimezone=Asia/Kolkata&allowPublicKeyRetrieval=true
spring.datasource.username=root
spring.datasource.password=YOUR_PASSWORD
# JPA/Hibernate Configuration
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
spring.jpa.properties.hibernate.format_sql=true
# JWT Configuration
jwt.secret.key=YOUR_SECRET_KEY_HEREmvn clean installmvn spring-boot:runThe application will start on http://localhost:8080
spring.datasource.url=jdbc:mysql://localhost:3306/expense_tracker
spring.jpa.show-sql=truespring.datasource.url=jdbc:mysql://your-production-db:3306/expense_tracker
spring.jpa.show-sql=falseGenerate a secure secret key:
openssl rand -base64 64Add to your properties file:
jwt.secret.key=<your-generated-key>http://localhost:8080
POST /api/auth/register
Content-Type: application/json
{
"username": "john_doe",
"email": "[email protected]",
"password": "SecurePass123!"
}POST /api/auth/login
Content-Type: application/json
{
"email": "[email protected]",
"password": "SecurePass123!"
}Response:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"userId": 1,
"email": "[email protected]",
"username": "john_doe"
}All expense endpoints require JWT authentication:
Authorization: Bearer <your-jwt-token>POST /api/expenses
Content-Type: application/json
{
"title": "Grocery Shopping",
"amount": 1500.00,
"date": "2024-10-23",
"category": "Food",
"description": "Weekly groceries"
}GET /api/expenses?page=0&size=10GET /api/expenses/{id}PUT /api/expenses/{id}
Content-Type: application/json
{
"title": "Updated Grocery Shopping",
"amount": 1600.00,
"date": "2024-10-23",
"category": "Food",
"description": "Weekly groceries - updated"
}DELETE /api/expenses/{id}POST /api/income
Content-Type: application/json
{
"title": "Monthly Salary",
"amount": 50000.00,
"date": "2024-10-01",
"category": "Salary",
"description": "October salary"
}GET /api/income?page=0&size=10PUT /api/income/{id}
Content-Type: application/jsonDELETE /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/chartGET /api/profilePUT /api/profile
Content-Type: application/json
{
"username": "john_doe_updated",
"email": "[email protected]"
}POST /api/profile/reset-password
Content-Type: application/json
{
"oldPassword": "OldPass123!",
"newPassword": "NewPass123!"
}CREATE TABLE user_entity (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(255) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE 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)
);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)
);- User registers/logs in with credentials
- Server validates credentials
- Server generates JWT token
- Client stores token (localStorage/sessionStorage)
- Client includes token in Authorization header for subsequent requests
- Server validates token and processes request
- Passwords are hashed using BCrypt algorithm
- Minimum password requirements enforced
- Secure password reset mechanism
Configure allowed origins in SecurityConfig.java:
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://localhost:3000"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}mvn testmvn clean test jacoco:reportThe API uses standard HTTP status codes:
| Status Code | Description |
|---|---|
| 200 | Success |
| 201 | Created |
| 400 | Bad Request |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not Found |
| 500 | Internal Server Error |
{
"timestamp": "2024-10-23T10:15:30",
"status": 400,
"error": "Bad Request",
"message": "Invalid input data",
"path": "/api/expenses"
}- Connection Pooling: HikariCP for efficient database connections
- Pagination: Implemented for large datasets
- Caching: Strategic caching for frequently accessed data
- Lazy Loading: Hibernate lazy loading for optimal performance
Create a Dockerfile:
FROM openjdk:17-jdk-slim
WORKDIR /app
COPY target/expense-tracker-0.0.1-SNAPSHOT.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]Build and run:
docker build -t expense-tracker-backend .
docker run -p 8080:8080 expense-tracker-backendThe application is ready to deploy on:
- AWS EC2
- Heroku
- Google Cloud Platform
- Azure
Contributions are welcome! Please follow these steps:
- Fork the repository
- Create a feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
- Follow Java naming conventions
- Write meaningful commit messages
- Add comments for complex logic
- Write unit tests for new features
This project is licensed under the MIT License - see the LICENSE file for details.
Your Name
- GitHub: @yourusername
- LinkedIn: Your LinkedIn
- Email: [email protected]
- Spring Boot Community
- JWT.io for authentication guidance
- MySQL Documentation
- Stack Overflow Community
For support, email [email protected] or open an issue in the repository.
β Star this repository if you find it helpful!
Made with β€οΈ and β