Track My Cash: Building a Secure, Decoupled Ledger with Spring Boot, Vanilla JS, and Gemini
Source: Dev.to
What I Built with Google Gemini
I created BookKeeping, a full‑stack financial ledger application that solves a very specific problem: tracking money I lend to friends without the overhead of a bloated accounting tool.
- Frontend – Vanilla JavaScript, HTML, and CSS (zero framework dependencies) deployed securely on Google Cloud Run.
- Backend – Java, Spring Boot 3, Spring Security 6, and a PostgreSQL database hosted on Render.
Google Gemini acted as my pair‑programming partner throughout the entire development cycle, helping me stitch together the tricky parts of a decoupled architecture.
How Gemini Helped
- Architect the Security Flow – Implemented stateless JWT authentication using HttpOnly cookies and standard Bearer tokens.
- Handle Browser Security Hurdles – Overcame aggressive third‑party cookie blocking (e.g., Safari ITP) by engineering a strict CORS and JSON‑payload strategy that neutralises CSRF attacks natively.
- Build the Math Engine – Co‑created a custom “Partial Repayment” algorithm that dynamically decreases loan amounts and automatically deletes database rows when a balance reaches zero.
- Construct Cloud Run Docker Environments – Built a lean Alpine‑Nginx container for the frontend. Gemini flagged that Cloud Run overrides standard port bindings, so we added a dynamic
nginx.conf.templatethat injects Cloud Run’s$PORTenvironment variable during deployment. - Design Native OS Localisation – Fixed a hard‑coded
'en-US'locale in the multi‑currency logic. By passingundefinedtoIntl.NumberFormat, the UI now inherits the operating system’s locale settings (e.g., proper Euro formatting).
Note on Authentication
The login system is intentionally straightforward. Since the app addresses a highly focused, personal task (tracking our own money), a complex OAuth or multi‑role hierarchy wasn’t necessary. The simple stateless JWT login keeps the app lightweight and fully under our control.
Demo
- GitHub Pages: https://rohithv07.github.io/BookKeeping/
- Cloud Run URL: (insert your Cloud Run URL here)
## Bookkeeping
Record the debts that we are lending to other people.
What Was Implemented
Spring Boot App
- A robust REST API built with Java 21 and Gradle.
Database Models
| Model | Fields |
|---|---|
| Borrower | name, email, phone |
| Loan | amount, dateLent, dueDate (exactly 1 month after lending), status (ACTIVE | REPAID) |
Core API Endpoints
| Method | Endpoint | Description |
|---|---|---|
POST | /api/borrowers | Create a new borrower |
GET | /api/borrowers | List all borrowers |
POST | /api/loans | Create a new loan |
GET | /api/loans | List all loans |
PUT | /api/loans/{id}/repay | Mark a loan as repaid |
Notification Job
NotificationServiceruns daily at 08:00 AM.- It queries for active loans whose
dueDateis today or earlier and sends an email to the personal Gmail account specified.
System Architecture Upgrades
- Interface‑Driven Design – Service layer uses contracts (
BorrowerService,LoanService) for loose coupling and easier scalability. - Data Transfer Objects (DTOs) – All API payloads are represented by
BorrowerDtoandLoanDto.
What I Learned
- Security & Distributed Environments – Separating the frontend (Google Cloud Run) from the backend (Render) highlighted cross‑domain cookie handling, pre‑flight
OPTIONSrequests, and browser privacy protections. - Multi‑Tenancy & Data Migration – Adding user‑profile mapping caused legacy data to appear “lost.” By collaborating with Gemini, we built a
DataMigrationRunnerthat scans PostgreSQL for orphaned records at startup and re‑associates them via new foreign keys. - Interactive Debugging – Instead of sifting through StackOverflow, pasting logs to Gemini let us quickly understand Nginx Alpine template processing and resolve a
gcloud run deployport‑binding error with a single variable‑substitution fix.
Google Gemini Feedback
What Worked Well
- Context Retention – Gemini kept track of complex file structures, allowing precise refactoring of the
JwtAuthenticationFilterto support both cookies and Bearer tokens. - Deep Architectural Awareness – This proved essential during PostgreSQL schema migrations and Cloud Run Nginx optimisations.
Where I Ran Into Friction
- Premature Fix Suggestions – Occasionally Gemini attempted backend fixes before I finished describing a frontend JavaScript issue (e.g., an
onclickmisfire caused by a simple string interpolation error). - Superficial Script Loops – The agent sometimes executed trivial internal scripts (e.g.,
echo "do something") to satisfy system task‑boundary checks before delivering the actual answer.