This is the backend service for NexaBudget, a personal finance management application. It provides a RESTful API for handling users, accounts, transactions, budgets, and categories.
- Java 25
- Spring Boot 4.0.0
- Spring Data JPA
- Spring Security with JWT Authentication
- Maven
- Lombok
- SpringDoc OpenAPI (Swagger UI)
- PostgreSQL
- MongoDB (for Vector Store / Semantic Cache)
- Valkey/Redis (for caching)
- Spring AI with Google Gemini AI (for transaction categorization)
- Spring Boot Actuator with Prometheus
- GraalVM Native Image support
The application uses a relational database to persist data. All entities use UUID as primary keys for better scalability and security. The main entities are:
-
User: Represents a user of the application.
id(Primary Key, UUID)username(unique)email(unique)password_hashcreated_atupdated_at
-
Account: Represents a financial account (e.g., bank account, credit card).
id(Primary Key, UUID)nametype(e.g., CASH, CHECKING, SAVINGS)currencyuser_id(Foreign Key to User, UUID)requisition_id(for GoCardless integration)external_account_id(for GoCardless integration)last_external_synccreated_at
-
Category: Represents a category for transactions (e.g., Food, Salary).
id(Primary Key, UUID)nametransaction_type(INCOME or EXPENSE)user_id(Foreign Key to User, UUID, nullable for default categories)
-
Transaction: Represents a single financial transaction.
id(Primary Key, UUID)amount(BigDecimal)descriptiontransaction_datetype(INCOME or EXPENSE)note(optional text field)user_id(Foreign Key to User, UUID)account_id(Foreign Key to Account, UUID)category_id(Foreign Key to Category, UUID)transfer_id(for linked transfers)external_id(for GoCardless integration)created_at
-
Budget: Represents a spending or saving goal for a specific category.
id(Primary Key, UUID)budget_limit(BigDecimal)start_dateend_datecategory_id(Foreign Key to Category, UUID)user_id(Foreign Key to User, UUID)created_at
-
CryptoHolding: Represents a cryptocurrency asset held by the user.
id(Primary Key, UUID)symbol(e.g., BTC, ETH)amount(BigDecimal)source(MANUAL or BINANCE)user_id(Foreign Key to User, UUID)
-
UserBinanceKeys: Stores encrypted API keys for Binance integration.
id(Primary Key, UUID)api_key(Encrypted)api_secret(Encrypted)user_id(Foreign Key to User, UUID)
To get a local copy up and running, follow these simple steps.
- JDK 25 or newer
- Maven
- Docker and Docker Compose (optional, for containerized deployment)
- MongoDB instance (for semantic caching)
git clone <your-repository-url>
cd nexaBudget-beYou can run the application either locally using Maven or with Docker.
This method is ideal for development and debugging.
You need a running instance of PostgreSQL. Set the following environment variables or update src/main/resources/application.properties:
# Database Configuration
spring.datasource.url=${DB_URL:jdbc:postgresql://localhost:5432/nexabudget}
spring.datasource.username=nexabudget-be
spring.datasource.password=${DB_PWD:your_password}
spring.datasource.driver-class-name=org.postgresql.Driver
# JPA Configuration
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.open-in-view=false
# JWT Configuration
app.jwtSecret=${JWT_SECRET:tua-chiave-segreta-molto-lunga-e-sicura-di-almeno-64-caratteri}
app.jwtExpirationInMs=86400000
# Crypto Encryption (for API Keys)
crypto.encryption.key=${CRYPTO_ENCRYPTION_KEY:MySuperSecretKey1234567890123456}
# GoCardless Integration
gocardless.integrator.baseUrl=http://localhost:3000
# Google Gemini AI Configuration
spring.ai.google.genai.api-key=${GEMINI_API_KEY:your_gemini_api_key}
spring.ai.google.genai.chat.options.model=gemini-2.5-flash-lite
spring.ai.google.genai.chat.options.temperature=0.1
# Semantic Cache / Vector Store (MongoDB)
spring.mongodb.uri=${MONGODB_URI:mongodb://localhost:27017/nexabudget-be}
spring.ai.vectorstore.mongodb.collection-name=semantic_cache
spring.ai.vectorstore.mongodb.index-name=semantic_cache_index
spring.ai.vectorstore.mongodb.initialize-schema=false
# Redis/Valkey Cache Configuration
spring.cache.type=redis
spring.data.redis.host=${REDIS_HOST:localhost}
spring.data.redis.port=${REDIS_PORT:6379}
spring.data.redis.password=${REDIS_PASSWORD:}
spring.data.redis.username=${REDIS_USERNAME:}
spring.data.redis.database=0
spring.data.redis.ssl.enabled=${REDIS_SSL_ENABLED:false}
# Actuator and Monitoring
management.endpoints.web.exposure.include=health,info,metrics,prometheus
management.endpoint.health.show-details=alwaysRequired Environment Variables:
DB_URL: PostgreSQL database URLDB_PWD: PostgreSQL database passwordGEMINI_API_KEY: Google Gemini API keyMONGODB_URI: MongoDB connection URI (for vector store)CRYPTO_ENCRYPTION_KEY: 32-char key for encrypting sensitive data (Binance keys)
Optional Environment Variables:
JWT_SECRET: Secret key for JWT signingREDIS_HOST: Redis/Valkey host (default: localhost)REDIS_PORT: Redis/Valkey port (default: 6379)REDIS_USERNAME: Redis/Valkey usernameREDIS_PASSWORD: Redis/Valkey passwordREDIS_SSL_ENABLED: Enable SSL for Redis connection (default: false)
Use Maven to build the project and download dependencies.
./mvnw clean installYou can run the application using the Spring Boot Maven plugin.
./mvnw spring-boot:runThe application will start on http://localhost:8080.
This is the recommended way to run the application in a production-like environment. The following commands will start the application, PostgreSQL database, Valkey cache, and MongoDB.
This command builds the JVM image and starts all services (app, PostgreSQL, Valkey, MongoDB) in detached mode.
docker-compose up --build -dFor better performance and a smaller memory footprint, you can run the native-compiled version.
docker-compose -f docker-compose.native.yml up --build -dIn both cases, the application will be available at http://localhost:8080, PostgreSQL at port 5432, Valkey at port 6379, and MongoDB at port 27017.
To stop and remove the containers, use:
# For JVM
docker-compose down
# For Native
docker-compose -f docker-compose.native.yml downOnce the application is running, you can access the Swagger UI documentation at:
- Swagger UI: http://localhost:8080/swagger-ui.html
- OpenAPI JSON: http://localhost:8080/v3/api-docs
The API provides the following endpoints:
POST /api/auth/login- User loginPOST /api/auth/register- User registration
GET /api/users/me- Get current user profilePUT /api/users/me- Update current user profile
GET /api/accounts- Get all user accountsPOST /api/accounts- Create new accountGET /api/accounts/{id}- Get account detailsPUT /api/accounts/{id}- Update accountDELETE /api/accounts/{id}- Delete account
GET /api/transactions- Get all user transactions (with pagination and filtering)POST /api/transactions- Create new transactionGET /api/transactions/{id}- Get transaction detailsPUT /api/transactions/{id}- Update transactionDELETE /api/transactions/{id}- Delete transactionPOST /api/transactions/{id}/categorize- AI categorization of transaction
GET /api/categories- Get all categories (default + user custom)POST /api/categories- Create custom categoryPUT /api/categories/{id}- Update categoryDELETE /api/categories/{id}- Delete category
GET /api/budgets- Get all user budgetsPOST /api/budgets- Create new budgetGET /api/budgets/{id}- Get budget detailsPUT /api/budgets/{id}- Update budgetDELETE /api/budgets/{id}- Delete budget
POST /api/gocardless/requisitions- Create requisition for bank account linkingGET /api/gocardless/requisitions/{id}- Get requisition statusGET /api/gocardless/requisitions/{id}/accounts- Get accounts from requisitionPOST /api/gocardless/sync/{accountId}- Sync transactions from external account
GET /api/crypto/portfolio- Get crypto portfolio value (supports currency conversion)POST /api/crypto/holdings- Add/Update manual crypto holdingPOST /api/crypto/binance/keys- Save Binance API keys (encrypted)POST /api/crypto/binance/sync- Trigger sync from Binance
All protected endpoints require a valid JWT token in the Authorization header:
Authorization: Bearer <your_jwt_token>The application uses JWT (JSON Web Tokens) for secure authentication. Tokens expire after 24 hours by default (configurable via app.jwtExpirationInMs property).
Transactions can be automatically categorized using Google's Gemini AI. The AI analyzes transaction descriptions and suggests the most appropriate category based on your existing categories.
Connect your real bank accounts through GoCardless (formerly Nordigen) to automatically import transactions. The integration supports:
- Creating bank connection requisitions
- Linking external accounts
- Syncing transactions from linked accounts
Track your cryptocurrency assets in one place:
- Binance Integration: securely connect your Binance account to auto-sync holdings.
- Manual Tracking: add assets from other wallets or exchanges manually.
- Portfolio Valuation: view your total portfolio balance in your preferred currency.
Uses Spring AI with MongoDB Atlas as a vector store to semantically cache AI responses, reducing costs and latency for repeated or similar queries.
The application uses Redisson, an advanced Redis client, to connect to Valkey (a Redis-compatible cache) to improve performance and reduce external API calls.
Why Redisson?
- π Superior performance with optimized connection pooling
- π§ Advanced features: distributed locks, collections, pub/sub
- π― Better error handling with automatic retries and failover
- π¦ Native JSON codec using Jackson
Configuration Approach:
- β
Programmatic configuration via
RedissonConfig.java - β Type-safe and compile-time verified
- β Automatic SSL support for production
- β No external YAML files needed
getBankAccounts: Caches bank account lists from GoCardless- Cache key:
requisitionId - Duration: 6 hours
- Cache key:
getGoCardlessTransaction: Caches transactions from GoCardless- Cache key:
requisitionId_accountId - Duration: 6 hours
- Cache key:
- π Faster response times for repeated queries
- π° Reduced API calls to external services
- β‘ Better scalability under load
- π Automatic retry and reconnection handling
For more details, see CACHE.md.
The application exposes Spring Boot Actuator endpoints for monitoring:
- Health: http://localhost:8080/actuator/health
- Metrics: http://localhost:8080/actuator/metrics
- Prometheus: http://localhost:8080/actuator/prometheus
The application supports compilation to a native executable using GraalVM, providing:
- Faster startup time
- Lower memory footprint
- Better resource efficiency
| Variable | Description | Required | Default |
|---|---|---|---|
DB_URL |
PostgreSQL database URL | Yes | jdbc:postgresql://localhost:5432/nexabudget |
DB_PWD |
PostgreSQL password | Yes | - |
GEMINI_API_KEY |
Google Gemini API key | Yes | - |
MONGODB_URI |
MongoDB Connection URI | Yes | mongodb://localhost:27017/nexabudget-be |
CRYPTO_ENCRYPTION_KEY |
Key for encrypting API keys (32 chars) | Yes | - |
REDIS_HOST |
Redis/Valkey host for caching | No | localhost |
REDIS_PORT |
Redis/Valkey port | No | 6379 |
REDIS_USERNAME |
Redis/Valkey username | No | (empty) |
REDIS_PASSWORD |
Redis/Valkey password | No | (empty) |
REDIS_SSL_ENABLED |
Enable SSL for Redis | No | false |
JWT_SECRET |
JWT signing secret key | No | tua-chiave-segreta-molto-lunga-e-sicura-di-almeno-64-caratteri |
app.jwtExpirationInMs |
JWT token expiration time in milliseconds | No | 86400000 (24 hours) |
gocardless.integrator.baseUrl |
GoCardless integrator service URL | No | http://localhost:3000 |
src/main/java/it/iacovelli/nexabudgetbe/
βββ config/ # Configuration classes
βββ controller/ # REST controllers
βββ dto/ # Data Transfer Objects
βββ model/ # JPA entities
βββ repository/ # Spring Data repositories
βββ security/ # JWT security components
βββ service/ # Business logic services
To build a native executable locally:
./mvnw -Pnative clean packageNote: This requires GraalVM to be installed and configured.