DEV Community

Cover image for How I Built an AI-Powered Email Ingestion System with Spring Boot, Angular and Gemini
Hari babu Thatikonda
Hari babu Thatikonda

Posted on

How I Built an AI-Powered Email Ingestion System with Spring Boot, Angular and Gemini

Letโ€™s be honest: searching for a job is a full-time job in itself. ๐Ÿ˜ฉ

Most of us rely on messy spreadsheets, generic Trello boards, or just "hoping for the best" to track applications. I wanted something better. I wanted a tool that didn't just list jobs, but actually automated the data entry, visualized my pipeline, and calculated my conversion rates.

So, I built JobTrackerPro.

I treated this as a System Design Challenge. Itโ€™s not just a CRUD app; itโ€™s an event-driven ecosystem featuring Generative AI, L2 Caching, and Cloud-Native Storage.


๐Ÿ”— The Goods


๐Ÿ› ๏ธ The Tech Stack

I chose a stack that balances raw performance with a modern developer experience:

  • Backend: Java 21 + Spring Boot 3.4
  • AI Orchestration: Google Gemini 2.0 Flash (Multilingual Extraction)
  • Security: Spring Security 6 (OAuth2 + JWT RS256)
  • Database: PostgreSQL 15 (Supabase) + Caffeine L2 Cache
  • Storage: Cloudflare R2 (S3 Compatible)
  • Frontend: Angular 17 (Signals) + TailwindCSS + D3.js
  • DevOps: Docker + Google Cloud Run + GitHub Actions

Dashboard


๐Ÿง  Core Engineering Challenges

1. ๐Ÿค– Zero-Entry Ingestion (Gemini 2.0 AI)

Manual data entry is where productivity goes to die. I engineered a pipeline that allows users to simply forward an application confirmation email to a unique system address.

  • The Flow: Gmail --> Cloudmailin Webhook --> Spring Boot --> Gemini 2.0 Flash.
  • Smart Upsert: The system uses fuzzy matching to detect if an email is a new job or an update (e.g., status change from "Applied" to "Interview") for an existing record.
  • Multilingual: If you apply for a job in the Netherlands (Dutch) or Germany (German), the AI automatically translates the metadata into English for your dashboard.
// Logic snippet for AI extraction using Spring Boot 3.4 RestClient
public JobDTO parseEmail(String rawBody) {
    String prompt = "Extract company, role, and status from this email. Return JSON...";
    // We send unstructured text; AI returns structured JobDTO
    var response = restClient.post()
        .uri(apiUrl)
        .body(requestBody)
        .retrieve()
        .body(String.class);
    return objectMapper.readValue(response, JobDTO.class);
}
Enter fullscreen mode Exit fullscreen mode

2. ๐Ÿ” Hybrid Security Architecture

Most tutorials make you choose: Social Login or Passwords. I wanted both. I implemented a custom OAuth2SuccessHandler that merges identities.

Users can sign in with Google/GitHub but also set a local password later. The system remains stateless using JWTs with high-precision expiration handling.

3. โšก High-Performance State & Caching

As the data grows, fetching 100+ job applications can lag. I solved this with two patterns:

  • Server-Side Everything: Pagination, Filtering, and Sorting are all done at the Database level using Spring Data Pageable.
  • Caffeine Cache: User profiles and dashboard statistics are stored in an L2 in-memory cache, reducing Supabase query latency to < 15ms.

4. โ˜๏ธ Atomic Cloud Storage (Cloudflare R2)

I integrated Cloudflare R2 for profile management. To ensure a clean bucket, I implemented Atomic Cleanup: whenever a user updates their photo, the backend automatically issues a deleteObject command to remove the old file, preventing "orphaned assets" and saving costs.


๐ŸŽจ Responsive Hybrid UI (Angular Signals)

On the frontend, I used Angular 17 Signals for fine-grained reactivity. The UI is a hybrid:

  • Desktop: A high-density data grid for power users.
  • Mobile: A custom Expandable Card System. Users can tap a card to reveal full details (notes, salary, links) and access large, touch-friendly action buttons.
Mobile View Desktop View
Mobile View Desktop View

๐Ÿ—๏ธ Open Source "Sandbox" Mode

I wanted to make this project easy for anyone to contribute to. I built a Developer Sandbox:

  1. Mock AI: If you don't have a Gemini API key, the app automatically switches to a Mock Service that returns realistic data.
  2. Local Storage: Files are saved to a local folder instead of R2.
  3. MailHog: All outgoing emails are trapped in a local dev UI.

You can run the entire system with one command:

docker-compose up -d && ./mvnw spring-boot:run
Enter fullscreen mode Exit fullscreen mode

Conclusion

Building JobTrackerPro taught me that "Full Stack" isn't just about connecting a UI with Backend. Itโ€™s about handling Timezones (forced UTC on JVM), XSS protection on extracted URLs, and ensuring Transactional Integrity between the Cloud and the DB.

Iโ€™m keeping this project Open Source. Feel free to fork it, star it, or use it to land your next job! ๐Ÿ‘‡

๐Ÿ’ฌ Discussion

How are you handling unstructured data ingestion in your apps? Have you switched from S3 to Cloudflare R2 yet? Let's talk in the comments!

Thanks for reading! Happy coding! ๐Ÿš€

Top comments (0)