A backend for a simplified real-time community charity auction application built with Java Spring Boot and MySQL. Users can list items for auction, place bids, and the system automatically manages the auction lifecycle.
- Getting Started
- Database Setup
- Build & Run
- Architecture & Design
- API Documentation
- API Endpoints
- WebSocket (Real-time Updates)
- Auction Closing Mechanism
- Concurrency Considerations
- Testing
- Postman Collection
- Java 25 (JDK 25)
- MySQL 8.0+
- Maven 3.9+ (or use the included Maven Wrapper)
| Technology | Purpose |
|---|---|
| Spring Boot 4.0.3 | Application framework |
| Spring Data JPA | Data persistence (Hibernate) |
| Spring Security | Basic security & password hashing |
| Spring WebSocket (STOMP) | Real-time bid notifications |
| Spring Validation | Input validation |
| MySQL | Relational database |
| Lombok | Boilerplate reduction |
| Springdoc OpenAPI | Swagger UI / API docs |
Connect to your MySQL server and create the database:
CREATE DATABASE IF NOT EXISTS auction_db;CREATE USER 'auction_user'@'localhost' IDENTIFIED BY 'your_password';
GRANT ALL PRIVILEGES ON auction_db.* TO 'auction_user'@'localhost';
FLUSH PRIVILEGES;The database connection is configured in src/main/resources/application.properties:
spring.datasource.url=jdbc:mysql://localhost:3306/auction_db?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
spring.datasource.username=root
spring.datasource.password=123456Update
usernameandpasswordto match your MySQL credentials.
Hibernate will auto-create/update the tables (spring.jpa.hibernate.ddl-auto=update), so no manual table creation is needed.
# Build the project
./mvnw clean package
# Run the application
./mvnw spring-boot:runmvn clean package
mvn spring-boot:runmvnw.cmd clean package
mvnw.cmd spring-boot:runThe application starts on http://localhost:8080.
- Health check:
GET http://localhost:8080/ - Swagger UI:
http://localhost:8080/swagger-ui.html
dev.test.realtime_auction_system/
├── config/ # Security & WebSocket configuration
│ ├── SecurityConfig - Spring Security (BCrypt, permit-all API)
│ └── WebSocketConfig - STOMP over SockJS configuration
├── controller/ # REST API controllers
│ ├── ItemController - Item CRUD & bidding endpoints
│ └── UserController - User registration endpoint
├── dto/ # Data Transfer Objects
│ ├── request/ - Validated request bodies
│ └── response/ - API response payloads
├── exception/ # Global exception handling
│ ├── GlobalExceptionHandler - @RestControllerAdvice
│ ├── ResourceNotFoundException
│ ├── AuctionClosedException
│ ├── InvalidBidException
│ └── DuplicateResourceException
├── model/ # JPA Entities
│ ├── User, Item, Bid - Core domain entities
│ └── AuctionStatus - ACTIVE / CLOSED enum
├── repository/ # Spring Data JPA repositories
│ ├── UserRepository
│ ├── ItemRepository - With pessimistic locking & specifications
│ └── BidRepository
├── scheduler/ # Background tasks
│ └── AuctionScheduler - Auto-closes expired auctions
├── service/ # Business logic
│ ├── UserService
│ ├── ItemService
│ └── BidService - Bid validation & WebSocket notification
├── specification/ # JPA Specifications for dynamic queries
│ └── ItemSpecification
├── HomeController.java # Health check endpoint
└── RealtimeAuctionSystemApplication.java # Main entry point
| Component | Type | Responsibility |
|---|---|---|
| ItemController | @RestController |
Handles all /items/** endpoints (CRUD, bidding, winner) |
| UserController | @RestController |
Handles /users/register and /users/{id} |
| ItemService | @Service |
Item creation, listing, search/filter, winner determination |
| BidService | @Service |
Bid validation, placement with pessimistic locking, WebSocket notification |
| UserService | @Service |
User registration with BCrypt password encoding |
| AuctionScheduler | @Component |
Background @Scheduled task that auto-closes expired auctions |
| GlobalExceptionHandler | @RestControllerAdvice |
Centralized error handling with structured JSON responses |
| ItemSpecification | Utility | JPA Specifications for dynamic search/filter queries |
User (1) ──── (*) Item [seller relationship]
User (1) ──── (*) Bid [bidder relationship]
Item (1) ──── (*) Bid [item-bid relationship]
- DTOs for all responses — Entities are never directly serialized to avoid circular references and to control the API surface.
- Pessimistic locking for bids —
@Lock(PESSIMISTIC_WRITE)onfindByIdForUpdate()prevents race conditions during concurrent bid placement. - Optimistic locking as defense-in-depth —
@Versionfield onItemprovides an additional safety layer. - Stateless security — CSRF disabled, no sessions; suitable for REST API consumption.
- Scheduler-based auction closing — A
@Scheduledtask runs every 10 seconds to close expired auctions, rather than checking on each request.
Once the app is running, visit:
| Method | Endpoint | Description | Request Body |
|---|---|---|---|
| POST | /users/register |
Register a new user | RegisterUserRequest |
| GET | /users/{userId} |
Get user details by ID | — |
Register User Request:
{
"username": "alice_seller",
"email": "alice@community.org",
"password": "securePass123"
}| Method | Endpoint | Description | Query Params |
|---|---|---|---|
| POST | /items |
Create a new auction item | — |
| GET | /items |
List active items (paginated & sorted) | page, size, sortBy, sortDir |
| GET | /items/search |
Search/filter items | keyword, status, minPrice, maxPrice, page, size, sortBy, sortDir |
| GET | /items/{itemId} |
Get item details + recent bid history | — |
Create Item Request:
{
"name": "Vintage Wall Clock",
"description": "A beautiful antique wall clock from the 1920s",
"startingPrice": 50.0,
"auctionEndTime": "2026-03-10T18:00:00",
"sellerId": 1
}| Method | Endpoint | Description | Request Body |
|---|---|---|---|
| POST | /items/{itemId}/bids |
Place a bid on item | PlaceBidRequest |
Place Bid Request:
{
"amount": 75.0,
"bidderId": 2
}Bid Validation Rules:
- Auction must be
ACTIVEand not past itsauctionEndTime - Bid must be >=
startingPrice(if first bid) - Bid must be >
currentHighestBid(if subsequent bid) - Seller cannot bid on their own item
| Method | Endpoint | Description |
|---|---|---|
| GET | /items/{itemId}/winner |
Get the winning bid (closed auctions only) |
All errors return a structured JSON response:
{
"status": 400,
"error": "Invalid Bid",
"message": "Bid amount must be higher than the current highest bid of 75.00",
"timestamp": "2026-03-05T14:30:00",
"validationErrors": null
}Validation errors include field-level details:
{
"status": 400,
"error": "Validation Failed",
"message": "Request body contains invalid fields",
"timestamp": "2026-03-05T14:30:00",
"validationErrors": {
"name": "Item name is required",
"startingPrice": "Starting price must be greater than 0"
}
}The backend supports real-time notifications using STOMP over WebSocket (with SockJS fallback).
WebSocket Endpoint: ws://localhost:8080/ws
| Topic | Event | Payload |
|---|---|---|
/topic/items/{itemId}/bids |
New high bid placed | BidResponse |
/topic/items/{itemId}/status |
Auction closed | Status message |
const socket = new SockJS("http://localhost:8080/ws");
const stompClient = Stomp.over(socket);
stompClient.connect({}, () => {
// Subscribe to bid updates for item 1
stompClient.subscribe("/topic/items/1/bids", (message) => {
const bid = JSON.parse(message.body);
console.log(`New high bid: $${bid.amount} by ${bid.bidderUsername}`);
});
// Subscribe to auction status changes for item 1
stompClient.subscribe("/topic/items/1/status", (message) => {
const status = JSON.parse(message.body);
console.log(`Auction ${status.status}: ${status.message}`);
});
});Note: No actual client-side implementation is included — only the backend WebSocket capability is provided.
Auctions are closed automatically by a background scheduler (AuctionScheduler):
- A
@Scheduledtask runs every 10 seconds (configurable viaauction.scheduler.rateinapplication.properties). - It queries for all
ACTIVEitems whereauctionEndTime <= NOW(). - Each expired item's status is set to
CLOSED. - A WebSocket notification is sent to
/topic/items/{itemId}/status. - The winning bid can then be retrieved via
GET /items/{itemId}/winner.
Why scheduled vs. on-demand?
- Decouples auction closing from user requests
- Ensures auctions close even if no one is actively querying them
- Central, predictable closing logic
- Pessimistic Locking: When placing a bid,
findByIdForUpdate()acquires aPESSIMISTIC_WRITElock on the item row. This ensures only one bid transaction proceeds at a time for the same item, preventing the "lost update" problem. - Optimistic Locking: The
Itementity has a@Versionfield as an additional safety net.
When two users bid simultaneously on the same item:
- Transaction A acquires a
PESSIMISTIC_WRITElock on the item row. - Transaction B blocks, waiting for the lock.
- Transaction A validates the bid, saves it, updates
currentHighestBid, and commits (releasing the lock). - Transaction B acquires the lock, reads the updated
currentHighestBid, and validates its bid against the new value.
In a production system with high traffic, consider:
- Distributed Locking — Use Redis-based locks (e.g., Redisson) if the application scales horizontally across multiple instances.
- Message Queues — Route bids through a message queue (RabbitMQ, Kafka) and process them sequentially per item.
- Event Sourcing — Store each bid as an immutable event and derive the current state, providing a complete audit trail.
- Database-level constraints — Add a CHECK constraint or trigger to enforce bid monotonicity at the database level as a final safeguard.
./mvnw test| Test Class | Covers |
|---|---|
BidServiceTest |
Bid validation, placement, error handling |
ItemServiceTest |
Item creation, listing, detail, winner |
UserServiceTest |
Registration, duplicate detection, retrieval |
AuctionSchedulerTest |
Scheduled auction closing logic |
A complete Postman collection is included at:
Realtime_Auction_System.postman_collection.json
- Open Postman → Import → Select the JSON file.
- The collection uses a
{{baseUrl}}variable defaulting tohttp://localhost:8080. - Run requests in order: Register users → Create items → Place bids → Check results.
- User registration (seller + bidders)
- Item creation with various auction end times
- Bidding with escalating amounts
- Error cases (validation, low bids, seller self-bidding, not found)
- Search and filter with pagination
- Auction winner retrieval
- Health check and Swagger access
Name: Gagana Methmal
LinkedIn: https://www.linkedin.com/in/gagana-methmal/
This project is licensed under the MIT License. See LICENSE for details.