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
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.
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:
- "What are you interested in?" — Selects topics like Java, Artificial Intelligence, Cybersecurity, etc.
- "How often do you want to receive news?" — Daily, Weekly, or Monthly.
- "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.
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).
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:
- Fetches news articles specific to the user's selected topics from the GNews API.
- Renders these articles into an HTML template using Thymeleaf.
- Sends the email.
All of this happens without freezing the user interface. The user instantly sees a "Successfully subscribed!" message on screen.
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.
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
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
.envfiles or the platform's dashboard.
Thanks to Springdoc OpenAPI (Swagger UI) integration, all REST API endpoints can be viewed and tested interactively through the browser.
| 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 |
| 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 |
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
┌──────────────────────┐ ┌──────────────────┐ ┌──────────────────────┐
│ 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)
Follow these steps in order to run the project on your local machine.
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 |
This project communicates with three external services. You need to create the following accounts for free:
- Go to supabase.com and create a free account.
- Create a new project (Region: EU Central recommended).
- After the project is created, navigate to Project Settings > Database.
- Get your JDBC connection details from Connection String > URI.
- The
DB_USERNAMEvalue will be inpostgres.PROJECT_IDformat (e.g.,postgres.lqmbyjzdcgxvbwjahpmg).
- Go to gnews.io.
- Sign in with your Google account for free.
- Copy the API Key provided on your dashboard.
- The free plan allows 100 requests per day — more than enough for development.
- Enable 2-Step Verification on your Gmail account.
- Go to the App Passwords page.
- Enter
NewsBrewas the app name and click "Create". - 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.
# Clone the backend repository
git clone https://github.com/YOUR_USERNAME/newsbrew.git
cd newsbrewCreate 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_hereYou can define environment variables directly through the IDE:
- Go to
Run > Edit Configurationsfrom the top menu. - Select the
NewsbrewApplicationconfiguration. - 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
- Click
Apply > OK.
# Build the project with Maven
mvn clean install -DskipTests
# Start the Spring Boot application
mvn spring-boot:runWhen started successfully, you'll see the following output in the terminal:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v4.1.0)
Started NewsbrewApplication in X.XXX seconds
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 devNavigate to http://localhost:5173 in your browser to start using the application.
If you don't want to install Java or Maven, you can run the project with a single command using Docker.
Fill out the .env file in the project root directory according to the format above.
cd newsbrew
docker-compose up -d --buildThis command will:
- Compile the Java project using Maven (multi-stage build).
- Copy the JAR file onto a lightweight Alpine Linux image.
- Start the container in the background.
# Check container status
docker ps
# Follow the logs
docker logs -f newsbrew-backenddocker-compose downWhile the application is running, you can access Swagger UI at:
🔗 http://localhost:8080/swagger-ui/index.html
| 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 |
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
}'"Tebrikler John Doe, bültene başarıyla abone oldunuz! İlk bülteniniz hazırlanıyor..."Note: Immediately after the response is returned, an
@Asyncbackground thread kicks in and sends the user their very first newsletter.
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} |
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
└─────────────────────────┘
Real-world problems encountered during the development of this project and how they were resolved:
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.
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.
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.
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).
- 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)
Contributions are welcome! To contribute to the project:
- Fork this repository.
- Create a new Feature Branch (
git checkout -b feature/amazing-feature). - Commit your changes (
git commit -m 'Add amazing feature'). - Push to the branch (
git push origin feature/amazing-feature). - Open a Pull Request.
This project is licensed under the MIT License — feel free to use, modify, and distribute it as you wish.

