A secure, multi-user expense tracking REST API with JWT authentication, category-based organization, and real-time filtering/reporting.
π Live API: https://expense-tracker-api-qur3.onrender.com
π Swagger UI: https://expense-tracker-api-qur3.onrender.com/swagger-ui/index.html
Note
Hosted on Render's free tier: The first request or visiting Swagger UI after a period of inactivity may take 30β50 seconds to respond while the server instance spin-up completes.
- JWT-Based Authentication: Secure endpoints with user registration, login, and token generation.
- Category Management: Create, read, update, and delete categories for expense classification.
- Expense Tracking:
- Add, update, view, and delete expenses tied directly to the authenticated user.
- Multi-criteria filtering by category and amount threshold.
- Paginated retrieval, sorting (ascending/descending by amount), and range-based filtering (by ID).
- Statistical endpoints like expense counts per category.
- API Documentation: Built-in interactive API exploration via Springdoc OpenAPI (Swagger UI).
- Docker Support: Containerized deployment setup using a multi-stage execution model.
- Backend Framework: Spring Boot
3.5.6 - Language: Java
21 - Security: Spring Security & JSON Web Tokens (
jjwtversion0.12.5) - Database: PostgreSQL (integrated with Neon Cloud Database)
- ORM / Persistence: Spring Data JPA & Hibernate
- API Documentation: Springdoc OpenAPI WebMVC UI (
2.8.13) - Boilerplate Reduction: Project Lombok
- Build Tool: Maven
Controller ββ> Service ββ> Repository ββ> Database
This application is built adhering to the standard multi-layered architecture guidelines. Controllers receive client HTTP requests, sanitize and validate input payloads using DTO validation constraints, and map them to business processes in the Service layer. The Service layer implements core logical actions and translates entities between client-facing DTO contracts and JPA models. The Repository layer handles DB operations via Spring Data JPA, ensuring clean segregation of concerns.
- Token-Bound Identity: User identity is derived server-side from the JWT claims β never trusted from client input β preventing users from accessing, modifying, or deleting other users' data.
- DTO Decoupling: Data Transfer Objects (DTOs) decouple the API contract from JPA entities, ensuring internal database schema modifications do not break the public-facing API.
- Centralized Exception Handling: A centralized global exception handler (
@ControllerAdvice) intercepts failures and returns structured, standardized JSON error responses instead of raw server stack traces.
expense-tracker/
βββ .mvn/ # Maven wrapper configuration
βββ src/
β βββ main/
β β βββ java/
β β β βββ com/example/expense_tracker/
β β β βββ controller/ # REST API controllers
β β β βββ dto/ # Data Transfer Objects (DTOs) for requests/responses
β β β βββ exception/ # Custom exceptions & global exception handler
β β β βββ model/ # JPA Entities (User, Category, Expenses)
β β β βββ repository/ # JPA Repositories
β β β βββ security/ # Spring Security, JWT filters & config
β β β βββ service/ # Business logic implementation
β β βββ resources/
β β βββ application.properties # Database & logging configurations
β βββ test/ # Test packages
βββ Dockerfile # Docker setup for packaging and running the app
βββ pom.xml # Maven dependencies and build plugins
βββ README.md # Project documentation
- Java Development Kit (JDK) 21
- Maven 3.x (or use the included
./mvnwwrapper) - A running PostgreSQL instance (local or hosted, e.g. Neon)
The application can read environment variables to override default database settings dynamically at runtime (useful for deployments on Render or Docker containers):
| Environment Variable | Description | Example Value |
|---|---|---|
JWT_SECRET |
Secret key used to sign and verify JSON Web Tokens (must be at least 256 bits) | mysecretkeymysecretkeymysecretkeymysecretkey123456789 |
SPRING_DATASOURCE_URL |
JDBC connection URL for PostgreSQL | jdbc:postgresql://ep-db-pooler.aws.neon.tech/expense_tracker?sslmode=require |
SPRING_DATASOURCE_USERNAME |
Username for database access | neondb_owner |
SPRING_DATASOURCE_PASSWORD |
Password for database access | your_secret_password |
- Navigate to the project root:
cd expense-tracker - Build and package the application:
./mvnw clean package
- Run the Spring Boot application:
./mvnw spring-boot:run
The application will start on port 8080 (default) and connect to the configured database.
- Build the Docker Image:
docker build -t expense-tracker-api . - Run the Container (overriding database properties with environment variables):
docker run -p 8080:8080 \ -e JWT_SECRET="your-custom-jwt-secret-key-at-least-256bits-long" \ -e SPRING_DATASOURCE_URL="jdbc:postgresql://your-db-host/db-name" \ -e SPRING_DATASOURCE_USERNAME="your-username" \ -e SPRING_DATASOURCE_PASSWORD="your-password" \ expense-tracker-api
Full interactive docs are available via Swagger UI at: https://expense-tracker-api-qur3.onrender.com/swagger-ui/index.html
| Method | Endpoint | Description |
|---|---|---|
POST |
/auth/register |
Register a new user with username and password |
POST |
/auth/login |
Authenticate username/password and receive a JWT token |
GET |
/auth/hello |
Public heartbeat/health check check (Returns "hello") |
| Method | Endpoint | Description |
|---|---|---|
POST |
/category |
Create a new category |
GET |
/category |
Retrieve all categories |
GET |
/category/{id} |
Get category details by its database ID |
PUT |
/category/{id} |
Update the name of an existing category |
DELETE |
/category/{id} |
Remove a category by its ID |
Important
All endpoints under /api/** require the header: Authorization: Bearer <token>
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/expenses |
Add a new expense for the authenticated user |
GET |
/api/expenses |
Get all expenses logged by the authenticated user |
GET |
/api/expenses/search |
Filter expenses globally by categoryId and/or minimum amount threshold |
GET |
/api/expenses/high-amount |
Filter expenses with an amount greater than a specified threshold |
GET |
/api/expenses/count |
Count the total number of expenses under a given category ID |
GET |
/api/expenses/filter |
Retrieve a paginated list of expenses filtered by category |
GET |
/api/expenses/id-range |
Retrieve a paginated list of expenses with IDs between id1 and id2 |
GET |
/api/expenses/sort |
Retrieve a paginated, sorted list of user expenses by amount |
GET |
/api/expenses/{id} |
Get details of a specific expense by ID |
PUT |
/api/expenses/{id} |
Update an existing expense by ID (must own the resource) |
DELETE |
/api/expenses/{id} |
Delete an existing expense by ID (must own the resource) |
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...
Content-Type: application/jsonRequest Body:
{
"categoryId": 1,
"amount": 25.50
}Response (201 Created):
{
"success": true,
"message": "Expense Added Successfully!",
"data": {
"id": 12,
"amount": 25.50,
"category": {
"id": 1,
"name": "Food & Dining"
}
}
}- Standardizing Response Envelopes: Adapt category and auth controllers to wrap responses in consistent DTO structures for standard API envelopes.
- Enhanced Test Coverage: Implement unit and integration tests using Spring Boot Starter Test to assert domain validations.
- Aggregated Expense Statistics: Introduce additional dashboards/reporting endpoints for monthly budget utilization summaries.
This project is licensed under the MIT License - see the LICENSE file for details.