Track My Cash: Building a Secure, Decoupled Ledger with Spring Boot, Vanilla JS, and Gemini

Published: (February 28, 2026 at 07:55 AM EST)
4 min read
Source: Dev.to

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.template that injects Cloud Run’s $PORT environment variable during deployment.
  • Design Native OS Localisation – Fixed a hard‑coded 'en-US' locale in the multi‑currency logic. By passing undefined to Intl.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

## 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

ModelFields
Borrowername, email, phone
Loanamount, dateLent, dueDate (exactly 1 month after lending), status (ACTIVE | REPAID)

Core API Endpoints

MethodEndpointDescription
POST/api/borrowersCreate a new borrower
GET/api/borrowersList all borrowers
POST/api/loansCreate a new loan
GET/api/loansList all loans
PUT/api/loans/{id}/repayMark a loan as repaid

Notification Job

  • NotificationService runs daily at 08:00 AM.
  • It queries for active loans whose dueDate is 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 BorrowerDto and LoanDto.

What I Learned

  • Security & Distributed Environments – Separating the frontend (Google Cloud Run) from the backend (Render) highlighted cross‑domain cookie handling, pre‑flight OPTIONS requests, 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 DataMigrationRunner that 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 deploy port‑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 JwtAuthenticationFilter to 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 onclick misfire 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.
0 views
Back to Blog

Related posts

Read more »

Google Gemini Writing Challenge

What I Built - Where Gemini fit in - Used Gemini’s multimodal capabilities to let users upload screenshots of notes, diagrams, or code snippets. - Gemini gener...