In a Nutshell
A Spring Boot REST API that powers Zero Gravity's emotion logging and AI analysis. The infrastructure and deployment pipeline were built on a free-tier cloud, and the AI features were designed within budget constraints.
- 5 domains, 14 endpoints: designed auth, emotion logging, statistics, and AI analysis into a single API.
- Built infrastructure with OCI + Terraform 6 modules, and achieved Zero-Downtime deployment with a Build-first strategy.
- Reduced Gemini API payload by 97% through Time Bucket sampling, keeping the AI feature within the free-tier budget.
From team project to production.
A Spring Boot backend that started as a team project was taken to production. The project originally had only basic CRUD APIs. Authentication, infrastructure, a deployment pipeline, and AI analysis were all added on top.
- Restructured the layered architecture into a domain-based structure.
- Implemented NextAuth OAuth to JWT authentication.
- Built infrastructure on OCI and implemented Zero-Downtime deployment.
- Added Gemini API-based emotion analysis.
API Endpoints
Infrastructure
Sending everything would blow the budget in a month.
The goal was to build a feature that analyzes emotion records via the Gemini API. Sending a full year of records meant ~55K input tokens per request. That was not sustainable on the project budget.
Reducing data would hurt analysis quality, but dropping the feature was not an option since it was core functionality.
One bucket, one representative record.
If sending everything was off the table, the solution was to pick the most representative record from each time period. The period was split into equal time units (buckets), and one representative record was taken per bucket. A Year analysis means monthly units, so twelve of them. Picking from the whole set at once would cluster them in the months with the most records, losing the shape of the year.
That left the question of how to define "representative." The criteria were already inside the service: the average emotion level and reason statistics the chart aggregated per period.
- Closest to the average emotion level
- Contains the most frequent reason
The reason statistics were re-aggregated per bucket, and the aggregation queries built for the emotion statistics were reused as they were.
Scored at 60% level, 40% reason.
Each bucket's representative was selected by a weighted score of emotion level 60% + reason match 40%.
Ties were broken by longest diary first, most reasons, then most recent.
Example: January bucket in a Year analysis (avg emotion level: 4.5, top reason: "Work")
97% payload reduction, $0.002 per request
Note: AI analysis results were cached for 24 hours, and the cache was invalidated for the relevant period whenever emotion records changed.