Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

32 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

PricePulse API

A Spring Boot-powered price tracking and notification service that monitors product prices and alerts users when target prices are reached.


Table of Contents


Overview

PricePulse API is a backend service that allows users to register products they want to track. The system periodically scrapes product prices from the web, stores the price history, and sends email notifications when prices drop below a defined threshold.

Main Features:

  • Automated product price scraping
  • Price history tracking and storage
  • Email alerts on price drops
  • Scheduled background price checks
  • Asynchronous processing via RabbitMQ

Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        Client / Frontend                         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                           β”‚ HTTP REST
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    ProductController                             β”‚
β”‚                  (REST API Layer)                                β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                           β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     ProductService                               β”‚
β”‚                  (Business Logic Layer)                          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
             β”‚                             β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  PriceUpdateScheduler β”‚    β”‚         RabbitMQ                   β”‚
β”‚  (Scheduled Tasks)    │───►│    (Message Queue)                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                           β”‚
                             β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                             β”‚       PriceCheckWorker             β”‚
                             β”‚    (Async Message Consumer)        β”‚
                             β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                    β”‚              β”‚
                     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                     β”‚ PriceScraper     β”‚  β”‚   EmailService       β”‚
                     β”‚ Service          β”‚  β”‚ (Notifications)      β”‚
                     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                    β”‚
                     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                     β”‚              Database (JPA)                  β”‚
                     β”‚   Product Table β”‚ PriceHistory Table         β”‚
                     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Project Structure

src/
└── main/
    β”œβ”€β”€ java/
    β”‚   └── com/meminksr/pricewatchdogapi/
    β”‚       β”œβ”€β”€ config/
    β”‚       β”‚   └── RabbitMQConfig.java          # RabbitMQ queues, exchanges & bindings
    β”‚       β”œβ”€β”€ controller/
    β”‚       β”‚   └── ProductController.java        # REST endpoints
    β”‚       β”œβ”€β”€ entity/
    β”‚       β”‚   β”œβ”€β”€ Product.java                  # Product JPA entity
    β”‚       β”‚   └── PriceHistory.java             # Price history JPA entity
    β”‚       β”œβ”€β”€ repository/
    β”‚       β”‚   β”œβ”€β”€ ProductRepository.java        # Product data access
    β”‚       β”‚   └── PriceHistoryRepository.java   # Price history data access
    β”‚       β”œβ”€β”€ service/
    β”‚       β”‚   β”œβ”€β”€ ProductService.java           # Core product business logic
    β”‚       β”‚   β”œβ”€β”€ PriceScraperService.java      # Web scraping logic
    β”‚       β”‚   β”œβ”€β”€ PriceCheckWorker.java         # RabbitMQ message consumer
    β”‚       β”‚   β”œβ”€β”€ PriceUpdateScheduler.java     # Cron-based scheduler
    β”‚       β”‚   └── EmailService.java             # Email notification sender
    β”‚       └── PriceWatchdogApiApplication.java  # Application entry point
    └── resources/
        β”œβ”€β”€ static/                               # Static assets
        β”œβ”€β”€ templates/                            # Email / view templates
        └── application.properties               # App configuration

️ Tech Stack

Layer Technology
Framework Spring Boot 3.x
Language Java 17+
ORM Spring Data JPA / Hibernate
Message Broker RabbitMQ (Spring AMQP)
Email Spring Mail (JavaMailSender)
Scheduling Spring @Scheduled
Web Scraping Jsoup (or similar)
Database (Development) H2 In-Memory (default)
Database (Production) PostgreSQL / MySQL (user-configured)
Build Tool Maven
API Testing HTTP Client (test.http)

Prerequisites

Before running this project, make sure you have the following installed:

  • Java 17+
  • Maven 3.8+
  • RabbitMQ (running locally or via Docker)
  • An SMTP email account (Gmail, etc.)
  • PostgreSQL / MySQL (only required for production β€” H2 is used automatically in development)

Quick start with Docker (RabbitMQ):

docker run -d \
  --name rabbitmq \
  -p 5672:5672 \
  -p 15672:15672 \
  rabbitmq:3-management

Access RabbitMQ dashboard at: http://localhost:15672 (guest / guest)


Getting Started

1. Clone the Repository

git clone https://github.com/meminksr/price-watchdog-api.git
cd price-watchdog-api

2. Configure Application Properties

Copy the template and fill in your own values:

cp src/main/resources/application.properties.example \
   src/main/resources/application.properties

Edit application.properties (see Configuration section below).

3. Build the Project

mvn clean install

4. Run the Application

mvn spring-boot:run

Or run the built JAR directly:

java -jar target/pricewatchdog-api-*.jar

The API will be available at http://localhost:8080.


Configuration

Development (Default β€” H2)

The project works out of the box with an H2 in-memory database. No extra setup required β€” tables are created automatically when the application starts.

Default src/main/resources/application.properties:

# ── Server ──────────────────────────────────────────
server.port=8080
# ── Database (H2 β€” Development) ─────────────────────
spring.datasource.url=jdbc:h2:mem:pricewatchdog
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=create-drop
spring.h2.console.enabled=true
# Access H2 console at: http://localhost:8080/h2-console
# ── RabbitMQ ─────────────────────────────────────────
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
# ── Email (SMTP) ─────────────────────────────────────
spring.mail.host=smtp.gmail.com
spring.mail.port=587
spring.mail.username=your_email@gmail.com
spring.mail.password=your_app_password
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
# ── Price Check Scheduler ────────────────────────────
price.check.cron=0 0 * * * *

What is H2? H2 is a lightweight embedded in-memory database that runs inside the JVM. It's ideal for development and testing, but all data is lost when the application stops. Use a persistent database for production.


πŸš€ Production β€” Add Your Own Database

If you're deploying the app or need persistent data, remove the H2 configuration and replace it with one of the options below.

PostgreSQL

1. Add the dependency to pom.xml:

<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <scope>runtime</scope>
</dependency>

2. Update application.properties:

spring.datasource.url=jdbc:postgresql://localhost:5432/pricewatchdog
spring.datasource.username=your_db_user
spring.datasource.password=your_db_password
spring.datasource.driver-class-name=org.postgresql.Driver
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=false

MySQL

1. Add the dependency to pom.xml:

<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
</dependency>

2. Update application.properties:

spring.datasource.url=jdbc:mysql://localhost:3306/pricewatchdog?useSSL=false&serverTimezone=UTC
spring.datasource.username=your_db_user
spring.datasource.password=your_db_password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=false

Quick PostgreSQL via Docker

Don't want to install PostgreSQL locally? Spin it up in seconds with Docker:

docker run -d \
  --name postgres \
  -e POSTGRES_DB=pricewatchdog \
  -e POSTGRES_USER=your_db_user \
  -e POSTGRES_PASSWORD=your_db_password \
  -p 5432:5432 \
  postgres:15

Security Note: Never commit credentials to version control. Use environment variables or a secrets manager in production.

# Example: using environment variables
spring.datasource.password=${DB_PASSWORD}
spring.mail.password=${MAIL_PASSWORD}

API Endpoints

The API is fully documented using OpenAPI (Swagger). You can access the interactive API documentation and test all endpoints directly from your browser.

Swagger UI Documentation

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

Swagger UI API Documentation

Base URL: http://localhost:8080/api

Products

Method Endpoint Description
GET /products List all tracked products
GET /products/{id} Get a single product by ID
POST /products Add a new product to track
PUT /products/{id} Update product details / target price
DELETE /products/{id} Remove a product from tracking

Price History

Method Endpoint Description
GET /products/{id}/price-history Get full price history for a product
GET /products/{id}/price-history/latest Get the most recent price entry

Example Requests

Add a product to track:

POST /api/products
Content-Type: application/json

{
  "name": "Sony WH-1000XM5",
  "url": "https://example.com/product/sony-wh1000xm5",
  "targetPrice": 250.00,
  "notificationEmail": "user@example.com"
}

Response:

{
  "id": 1,
  "name": "Sony WH-1000XM5",
  "url": "https://example.com/product/sony-wh1000xm5",
  "currentPrice": 299.99,
  "targetPrice": 250.00,
  "notificationEmail": "user@example.com",
  "createdAt": "2025-05-13T10:00:00"
}

Get price history:

GET /api/products/1/price-history
[
  {
    "price": 320.00,
    "checkedAt": "2025-05-10T08:00:00"
  },
  {
    "price": 299.99,
    "checkedAt": "2025-05-11T08:00:00"
  },
  {
    "price": 280.00,
    "checkedAt": "2025-05-12T08:00:00"
  }
]

How It Works

1. User registers a product URL with a target price
         β”‚
         β–Ό
2. PriceUpdateScheduler triggers on schedule (e.g. every hour)
         β”‚
         β–Ό
3. Scheduler publishes a price-check message to RabbitMQ
         β”‚
         β–Ό
4. PriceCheckWorker consumes the message asynchronously
         β”‚
         β–Ό
5. PriceScraperService fetches & parses the product page
         β”‚
         β–Ό
6. New price is saved to PriceHistory table
         β”‚
         β”œβ”€β”€ [Price ≀ Target] ──► EmailService sends alert to user 
         β”‚
         └── [Price > Target] ──► No action, wait for next cycle

Key Components

RabbitMQConfig

Declares the exchange, queue, and binding used by the price-check workflow. Enables asynchronous, decoupled communication between the scheduler and worker.

ProductController

Exposes REST endpoints for CRUD operations on tracked products and their price histories. Delegates business logic to ProductService.

ProductService

Orchestrates product registration, update, and deletion. Coordinates with repositories for persistence.

PriceScraperService

Fetches the HTML of a product page and extracts the current price using CSS selectors or XPath. Handles retries and HTTP errors gracefully.

PriceUpdateScheduler

A @Scheduled component that runs at a configured interval and publishes a job message to RabbitMQ for every active tracked product.

PriceCheckWorker

A RabbitMQ message listener (@RabbitListener) that consumes price-check jobs. Calls the scraper, persists the result, and triggers email notifications when appropriate.

EmailService

Sends HTML-formatted notification emails via JavaMailSender when a product's price falls at or below the user's target price.


Message Queue Flow

[PriceUpdateScheduler]
        β”‚
        β”‚  publish: { productId: 42 }
        β–Ό
[RabbitMQ Exchange: price.check.exchange]
        β”‚
        β”‚  routing key: price.check
        β–Ό
[Queue: price.check.queue]
        β”‚
        β–Ό
[PriceCheckWorker] ──► scrape ──► save ──► notify (if needed)

Exchange Type: Direct Queue: price.check.queue Routing Key: price.check


Database Schema

product table

Column Type Description
id BIGINT (PK) Auto-generated ID
name VARCHAR Product display name
url TEXT Product page URL
current_price DECIMAL Last scraped price
target_price DECIMAL User-defined alert threshold
notification_email VARCHAR Recipient for alerts
created_at TIMESTAMP Registration time
updated_at TIMESTAMP Last update time

price_history table

Column Type Description
id BIGINT (PK) Auto-generated ID
product_id BIGINT (FK) Reference to product
price DECIMAL Scraped price value
checked_at TIMESTAMP When price was recorded

Contributing

Contributions are welcome! Please follow these steps:

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/your-feature-name
  3. Commit your changes: git commit -m "feat: add your feature"
  4. Push to your branch: git push origin feature/your-feature-name
  5. Open a Pull Request

Please follow the existing code style and write unit tests for new functionality.


License

This project is licensed under the MIT License. See the LICENSE file for details.


Made with β˜• and Spring Boot by meminksr

About

A robust e-commerce price tracking platform built with a microservices architecture using Java Spring Boot, React, Node.js, and RabbitMQ. Features include automated asynchronous price scraping via Playwright, proxy API integration for bypassing bot protections, scheduled background jobs for price updates, and a PostgreSQL database.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages