Skip to content

Repository files navigation

📰 NewsBrew

Personalized Tech Newsletter Platform

Spring Boot Java PostgreSQL Vite Docker Swagger

NewsBrew is an intelligent platform that curates the latest technology news from around the world based on users' interests and delivers personalized newsletters via automated emails. The system runs completely autonomously — once a user subscribes, background scheduled tasks (Cron Jobs) and asynchronous threads handle everything automatically.

Features · Architecture · Getting Started · API Documentation · Docker


Screenshots

Frontend (Test Interface)

Note: The frontend is a simple test interface built to interact with and test the backend API. It is not a production-grade client application.

Frontend — Subscription Form

Email Newsletter Output

Email — Personalized Newsletter


🧠 The Idea Behind the Project

As a developer today, it's nearly impossible to keep up with dozens of different sources (Hacker News, Dev.to, Medium, TechCrunch...) and never miss breaking news in your areas of interest. NewsBrew was born to solve exactly this problem.

When subscribing to the platform, the user answers three fundamental questions:

  1. "What are you interested in?" — Selects topics like Java, Artificial Intelligence, Cybersecurity, etc.
  2. "How often do you want to receive news?" — Daily, Weekly, or Monthly.
  3. "Which day and time?" — For example, every Sunday at 9:00 AM.

Once this information is saved to the database, the system starts running entirely on its own. Every hour, it wakes up in the background, checks "Who should I send a newsletter to right now?", fetches the freshest news from the GNews API, filters them according to the user's selected topics, generates a sleek HTML email template, and sends it out.


Features

Smart Scheduler

The system leverages Spring Boot's built-in cron job infrastructure via the @Scheduled annotation. The NewsletterJob component runs every hour, identifying users who are due for their newsletter. It performs intelligent filtering based on the user's chosen frequency (Daily/Weekly/Monthly) and day/time preferences:

  • Daily subscribers → Receive their newsletter every day at their chosen hour.
  • Weekly subscribers → Receive their newsletter only on their selected day and hour (e.g., Sunday at 9:00 AM).
  • Monthly subscribers → Receive their newsletter on their selected day of the month (e.g., 15th of each month at 10:00 AM).

⚡ Asynchronous Welcome Email

When a user subscribes, they don't have to wait until Sunday or their scheduled time. Using Spring's @Async annotation, a new thread is spawned independently from the main thread. This background thread:

  1. Fetches news articles specific to the user's selected topics from the GNews API.
  2. Renders these articles into an HTML template using Thymeleaf.
  3. Sends the email.

All of this happens without freezing the user interface. The user instantly sees a "Successfully subscribed!" message on screen.

🌍 Autonomous Data Aggregation (RSS Engine)

Instead of relying on third-party APIs like GNews (which often have rate limits or block cloud deployments), NewsBrew has its own Data Aggregation Engine. The system automatically connects to the RSS feeds of the world's most popular technology sites (TechCrunch, The Verge, AWS Blog, etc.), parses the XML data using Rome Tools, cleans the HTML content with Jsoup, and stores the articles in the PostgreSQL database. We don't fetch news; we own the news. All external fetch calls are wrapped in try-catch blocks — if a feed is down, the system gracefully continues with the remaining sources.

Dynamic HTML Email Templates (Thymeleaf)

The emails sent are not plain text. Using the Thymeleaf template engine, a personalized, image-rich, linked, and responsive HTML newsletter is generated for each user. Every email includes:

  • Article title, summary, and cover image
  • A "Read Article" button linking to the original source
  • An unsubscribe link

Secure Architecture

Sensitive data (database password, API keys, Gmail app password) is never hardcoded in the source code. All secrets are read via ${ENV_VARIABLE} syntax through Environment Variables. This ensures:

  • The project can be safely shared as a Public repository on GitHub.
  • In Docker, Render, or any cloud provider, secrets are managed securely through .env files or the platform's dashboard.

API Documentation (Swagger UI)

Thanks to Springdoc OpenAPI (Swagger UI) integration, all REST API endpoints can be viewed and tested interactively through the browser.


Architecture & Tech Stack

Backend

Technology Purpose
Java 21 (LTS) Primary programming language
Spring Boot 4.1.0 REST API, Dependency Injection, Auto-Configuration
Spring Data JPA & Hibernate 7 ORM (Object-Relational Mapping), database operations
PostgreSQL (Supabase) Cloud-based relational database
Spring Boot Mail Email delivery via SMTP
Thymeleaf Server-side dynamic HTML template rendering
Spring @Scheduled Scheduled tasks via cron jobs
Spring @Async Background asynchronous thread management
Springdoc OpenAPI (Swagger UI) Interactive API documentation
Lombok Boilerplate code reduction (Getter/Setter/Constructor)
Docker & Docker Compose Container-based deployment and execution

Frontend

Technology Purpose
Vite 5 Fast development server and build tool
Vanilla JavaScript Client-side business logic
HTML5 & CSS3 Structure and styling
Glassmorphism UI Modern, transparent, dark-mode-enabled interface

Project Structure

newsbrew/
├── src/main/java/com/meminksr/newsbrew/
│   ├── NewsbrewApplication.java          # Main application entry point (@EnableAsync, @EnableScheduling)
│   │
│   ├── controller/
│   │   ├── UserController.java           # User subscribe/unsubscribe endpoints
│   │   └── TopicController.java          # Topic listing endpoint
│   │
│   ├── entity/
│   │   ├── User.java                     # User entity (JPA) - @ManyToMany relationship
│   │   ├── Topic.java                    # Topic entity (Java, AI, Cloud, etc.)
│   │   ├── Article.java                  # Article entity - @ManyToOne relationship
│   │   └── ScheduleFrequency.java        # Enum: DAILY, WEEKLY, MONTHLY
│   │
│   ├── dto/
│   │   ├── UserSubscribeRequest.java     # Subscription request data transfer object
│   │   ├── NewsApiResponse.java          # GNews API response model
│   │   └── NewsArticleDto.java           # Individual article data transfer object
│   │
│   ├── repository/
│   │   ├── UserRepository.java           # User database queries (JPA Repository)
│   │   ├── TopicRepository.java          # Topic database queries
│   │   └── ArticleRepository.java        # Article database queries
│   │
│   └── service/
│       ├── NewsFetcherService.java        # GNews API news fetching service
│       ├── NewsletterSenderService.java   # Email generation and delivery service (@Async)
│       └── NewsletterJob.java             # Scheduled task manager (@Scheduled cron job)
│
├── src/main/resources/
│   ├── application.properties             # Application configuration (DB, Mail, API)
│   └── templates/
│       └── newsletter.html                # Thymeleaf HTML email template
│
├── Dockerfile                             # Multi-stage Docker build file
├── docker-compose.yml                     # Docker Compose configuration
├── .env                                   # Environment variables template (DO NOT COMMIT!)
├── .dockerignore                          # Files excluded from Docker build
└── pom.xml                                # Maven dependency management

Database Schema (ER Diagram)

┌──────────────────────┐       ┌──────────────────┐       ┌──────────────────────┐
│        users         │       │   user_topics     │       │       topics         │
├──────────────────────┤       ├──────────────────┤       ├──────────────────────┤
│ id (PK, BIGINT)      │──────>│ user_id (FK)      │<──────│ id (PK, BIGINT)      │
│ name (VARCHAR)       │       │ topic_id (FK)     │       │ name (VARCHAR, UNIQUE)│
│ email (VARCHAR, UQ)  │       └──────────────────┘       └──────────┬───────────┘
│ is_active (BOOLEAN)  │                                             │
│ schedule_frequency   │                                             │
│ schedule_day (INT)   │       ┌──────────────────────┐              │
│ schedule_hour (INT)  │       │      articles        │              │
└──────────────────────┘       ├──────────────────────┤              │
                               │ id (PK, BIGINT)      │              │
                               │ title (TEXT)          │              │
                               │ description (TEXT)    │              │
                               │ url (TEXT)            │              │
                               │ image_url (TEXT)      │              │
                               │ published_at (TIMESTAMP)│            │
                               │ topic_id (FK) ────────┼──────────────┘
                               └──────────────────────┘

Relationships:
  users  <──  ManyToMany  ──>  topics     (via user_topics junction table)
  topics <──  OneToMany   ──>  articles   (each topic can have multiple articles)

Getting Started

Follow these steps in order to run the project on your local machine.

Prerequisites

Make sure the following tools are installed on your computer before you begin:

Tool Minimum Version Download Link
Java JDK 21+ Eclipse Temurin
Maven 3.9+ Apache Maven
Git 2.x Git SCM
Node.js (for Frontend) 18+ Node.js
Docker (Optional) 24+ Docker Desktop

External Service Accounts

This project communicates with three external services. You need to create the following accounts for free:

1. Supabase (PostgreSQL Database)

  1. Go to supabase.com and create a free account.
  2. Create a new project (Region: EU Central recommended).
  3. After the project is created, navigate to Project Settings > Database.
  4. Get your JDBC connection details from Connection String > URI.
  5. The DB_USERNAME value will be in postgres.PROJECT_ID format (e.g., postgres.lqmbyjzdcgxvbwjahpmg).

2. GNews API (News Source)

  1. Go to gnews.io.
  2. Sign in with your Google account for free.
  3. Copy the API Key provided on your dashboard.
  4. The free plan allows 100 requests per day — more than enough for development.

3. Gmail SMTP (Email Delivery)

  1. Enable 2-Step Verification on your Gmail account.
  2. Go to the App Passwords page.
  3. Enter NewsBrew as the app name and click "Create".
  4. Google will give you a 16-character App Password (e.g., abcd efgh ijkl mnop). Copy this password.

IMPORTANT: This app password is NOT your Gmail account password. It's a special password generated by Google specifically for third-party applications.


Step 1: Clone the Repository

# Clone the backend repository
git clone https://github.com/YOUR_USERNAME/newsbrew.git
cd newsbrew

Step 2: Set Up Environment Variables

Create a .env file in the project root directory:

# .env file
DB_USERNAME=postgres.YOUR_PROJECT_ID_HERE
DB_PASSWORD=your_supabase_password_here
NEWS_API_KEY=your_gnews_api_key_here
MAIL_USERNAME=your_gmail_address@gmail.com
MAIL_PASSWORD=your_gmail_app_password_here

If Using IntelliJ IDEA:

You can define environment variables directly through the IDE:

  1. Go to Run > Edit Configurations from the top menu.
  2. Select the NewsbrewApplication configuration.
  3. In the Environment Variables field, enter the following format:
DB_USERNAME=postgres.xxx;DB_PASSWORD=xxx;NEWS_API_KEY=xxx;MAIL_USERNAME=xxx@gmail.com;MAIL_PASSWORD=xxx
  1. Click Apply > OK.

Step 3: Build and Run the Project

# Build the project with Maven
mvn clean install -DskipTests

# Start the Spring Boot application
mvn spring-boot:run

When started successfully, you'll see the following output in the terminal:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/

 :: Spring Boot ::                (v4.1.0)

Started NewsbrewApplication in X.XXX seconds

Step 4: Run the Frontend

Open a new terminal window:

# Clone the frontend repository
git clone https://github.com/YOUR_USERNAME/newsbrew-frontend.git
cd newsbrew-frontend

# Install dependencies
npm install

# Start the development server
npm run dev

Navigate to http://localhost:5173 in your browser to start using the application.


Running with Docker

If you don't want to install Java or Maven, you can run the project with a single command using Docker.

Step 1: Prepare the .env File

Fill out the .env file in the project root directory according to the format above.

Step 2: Launch with Docker Compose

cd newsbrew
docker-compose up -d --build

This command will:

  1. Compile the Java project using Maven (multi-stage build).
  2. Copy the JAR file onto a lightweight Alpine Linux image.
  3. Start the container in the background.

Verification

# Check container status
docker ps

# Follow the logs
docker logs -f newsbrew-backend

Stopping

docker-compose down

API Documentation

While the application is running, you can access Swagger UI at:

🔗 http://localhost:8080/swagger-ui/index.html

Available Endpoints

Method Endpoint Description
POST /api/users/subscribe Creates a new user subscription
GET /api/users/unsubscribe?email=x Deactivates a user's subscription
GET /api/topics Lists all available topics

Example Request: Subscribe

curl -X POST http://localhost:8080/api/users/subscribe \
  -H "Content-Type: application/json" \
  -d '{
    "name": "John Doe",
    "email": "john@example.com",
    "topicIds": [1, 3, 5],
    "scheduleFrequency": "WEEKLY",
    "scheduleDay": 7,
    "scheduleHour": 9
  }'

Example Response

"Tebrikler John Doe, bültene başarıyla abone oldunuz! İlk bülteniniz hazırlanıyor..."

Note: Immediately after the response is returned, an @Async background thread kicks in and sends the user their very first newsletter.


Configuration Reference

All configuration keys in the application.properties file:

Key Description Default
spring.datasource.url PostgreSQL JDBC connection URL Supabase Pooler URL
spring.datasource.username Database username ${DB_USERNAME}
spring.datasource.password Database password ${DB_PASSWORD}
spring.jpa.hibernate.ddl-auto Hibernate schema management strategy update
news.api.key GNews API key ${NEWS_API_KEY:none}
spring.mail.host SMTP server smtp.gmail.com
spring.mail.port SMTP port 587
spring.mail.username Sender Gmail address ${MAIL_USERNAME:none}
spring.mail.password Gmail app password ${MAIL_PASSWORD:none}

System Flow Diagram

User Subscribes
        │
        ▼
┌─────────────────────────┐
│   UserController        │
│   POST /subscribe       │──────── 200 OK (Instant response)
└────────────┬────────────┘
             │ @Async (Background thread starts)
             ▼
┌─────────────────────────┐
│  NewsFetcherService     │
│  fetchNewsForTopics()   │──── Fetches news only for the user's selected topics
└────────────┬────────────┘
             │
             ▼
┌─────────────────────────┐
│  NewsletterSenderService│
│  sendWelcomeNewsletter  │──── Renders HTML with Thymeleaf
│  Async()                │──── Sends email via JavaMailSender
└─────────────────────────┘


═══════════════════════════════════════════════════

Every Hour (Cron Job)
        │
        ▼
┌─────────────────────────┐
│   NewsletterJob         │
│   @Scheduled            │──── Filters active users by current hour
│   (cron = "0 0 * * * *")│
└────────────┬────────────┘
             │
             ▼
┌─────────────────────────┐
│  Frequency Check        │
│  DAILY  → Send every day │
│  WEEKLY → If day matches │
│  MONTHLY→ If day matches │
└────────────┬────────────┘
             │
             ▼
┌─────────────────────────┐
│  sendNewsletter         │
│  ToSingleUser()         │──── Sends personalized newsletter
└─────────────────────────┘

Challenges Encountered & Solutions

Real-world problems encountered during the development of this project and how they were resolved:

1. Supabase SNI (Tenant Identifier) Error

Problem: Supabase PgBouncer threw ENOIDENTIFIER: no tenant identifier provided for connections without SSL.

Solution: Added ?sslmode=require parameter to the JDBC connection URL and updated the port to 6543.

2. NewsAPI Cloud Server Restriction

Problem: NewsAPI's free plan only allows requests from localhost. Requests from cloud servers (Render, AWS, etc.) returned a 426 Upgrade Required error.

Solution: The news provider was fully migrated to GNews API. GNews does not block requests from cloud servers on its free plan.

3. Silent Crash in Async Email Delivery

Problem: Unhandled API errors within the @Async thread caused the background process to crash silently — no email was sent and no error message was visible.

Solution: All external API calls were wrapped in try-catch blocks. When an error occurs, the system gracefully continues with existing articles from the database instead of crashing.

4. Render SMTP Port Blocking

Problem: Render's free tier blocks standard email ports (587, 465, 25) via its firewall.

Solution: Gmail SMTP works flawlessly in the local development environment. For cloud deployment, the infrastructure is ready for migration to REST-based email providers (Resend, Brevo).


🛣 Roadmap

  • REST-based email provider integration (Resend or Brevo)
  • User dashboard (view past newsletters)
  • Topic recommendation system (Collaborative Filtering)
  • Rate limiting and API usage monitoring
  • Unit and Integration tests
  • CI/CD pipeline (GitHub Actions)

Contributing

Contributions are welcome! To contribute to the project:

  1. Fork this repository.
  2. Create a new Feature Branch (git checkout -b feature/amazing-feature).
  3. Commit your changes (git commit -m 'Add amazing feature').
  4. Push to the branch (git push origin feature/amazing-feature).
  5. Open a Pull Request.

License

This project is licensed under the MIT License — feel free to use, modify, and distribute it as you wish.


About

Kullanıcıların seçtiği ilgi alanlarına göre RSS akışlarından (TechCrunch, Dev.to, InfoQ vb.) anlık teknoloji haberleri toplayan ve @scheduled cron job'lar ile otomatik e-posta bülteni gönderen akıllı bir platform. Spring Boot, Supabase, Rome Tools, Jsoup ve asenkron (@async) mimaride geliştirilmiştir. Süreçler tamamen otonom ve ölçeklenebilir yapıd

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages