QuickBuy is a secured Spring Boot e-commerce REST API for product catalog management, image storage, inventory-aware order placement, and customer order history.
- Stateless JWT bearer authentication with BCrypt password hashing
- Customer and administrator authorization
- Product CRUD, search, and multipart image upload
- Transactional order placement with pessimistic inventory locking
- Request validation and consistent JSON error responses
- PostgreSQL persistence through Spring Data JPA
- OpenAPI documentation with Swagger UI
- 16 JUnit 5, Mockito, MockMvc, and H2 tests with 70.5% line coverage
- Concurrent PowerShell load test with latency percentiles
- Java 21
- Spring Boot 4.1
- Spring Web MVC, Security, Validation, and Data JPA
- PostgreSQL; H2 for tests
- JJWT 0.12.6
- springdoc-openapi 3.1.0
- JaCoCo 0.8.15
- Maven and Lombok
The application reads configuration from environment variables and provides development defaults:
| Variable | Default | Purpose |
|---|---|---|
DB_URL |
jdbc:postgresql://localhost:5432/products |
PostgreSQL JDBC URL |
DB_USERNAME |
postgres |
Database username |
DB_PASSWORD |
Required | Database password |
JWT_SECRET |
Required | Base64-encoded key of at least 256 bits |
JWT_EXPIRATION_MS |
900000 |
Token lifetime in milliseconds |
CORS_ALLOWED_ORIGINS |
http://localhost:3000 |
Comma-separated browser origins |
Set a strong secret before running the application. One way to generate and configure it is:
$bytes = New-Object byte[] 32
[Security.Cryptography.RandomNumberGenerator]::Fill($bytes)
$env:JWT_SECRET = [Convert]::ToBase64String($bytes)
$env:DB_PASSWORD = "your-postgres-password"Create the local database before starting the application:
CREATE DATABASE products;.\mvnw.cmd spring-boot:runThe API starts at http://localhost:8080.
Register a customer:
POST /register
Content-Type: application/json
{
"name": "nikhil",
"password": "StrongPass123"
}Log in:
POST /login
Content-Type: application/json
{
"name": "nikhil",
"password": "StrongPass123"
}The response contains a token:
{
"token": "eyJ...",
"type": "Bearer",
"expiresInSeconds": 900
}Use it for protected requests:
Authorization: Bearer eyJ...New registrations receive the CUSTOMER role. For local development, promote a user to administrator directly in PostgreSQL:
UPDATE users SET role = 'ADMIN' WHERE name = 'nikhil';| Method | Endpoint | Access | Description |
|---|---|---|---|
| POST | /register |
Public | Register a customer |
| POST | /login |
Public | Obtain a JWT |
| GET | /api/products |
Authenticated | List products |
| GET | /api/product/{id} |
Authenticated | Get one product |
| GET | /api/products/search?keyword=... |
Authenticated | Search products |
| GET | /api/product/{id}/image |
Authenticated | Retrieve product image |
| POST | /api/product |
Admin | Create a product |
| PUT | /api/product/{id} |
Admin | Update a product |
| DELETE | /api/product/{id} |
Admin | Delete a product |
| POST | /api/orders/place |
Authenticated | Place an order |
| GET | /api/orders/my |
Authenticated | List the current user's orders |
| GET | /api/orders |
Admin | List all orders |
Product create/update requests use multipart/form-data with a JSON part named product and an image part named imageFile. Updates may omit imageFile to keep the existing image.
{
"name": "iPhone 15",
"description": "Apple smartphone",
"brand": "Apple",
"price": 79999,
"category": "Mobile",
"releaseDate": "2026-01-10",
"productAvailable": true,
"stockQuantity": 20
}Order request example:
{
"customerName": "Nikhil",
"email": "nikhil@example.com",
"items": [
{
"productId": 1,
"quantity": 2
}
]
}Order creation is transactional. Products are locked while stock is checked and reduced, preventing partial orders and concurrent overselling.
After starting the application:
- Swagger UI:
http://localhost:8080/swagger-ui.html - OpenAPI JSON:
http://localhost:8080/v3/api-docs - OpenAPI YAML:
http://localhost:8080/v3/api-docs.yaml
Use /login, copy the returned token, select Authorize in Swagger UI, and enter the token.
Run the complete suite and generate the coverage report:
.\mvnw.cmd verifyThe 16 tests use an in-memory H2 database and do not require PostgreSQL. The suite achieves 70.5% line coverage and 70.9% instruction coverage, measured with JaCoCo. After the build, open target/site/jacoco/index.html for the detailed report.
Test coverage includes:
- Application startup
- JWT generation and verification
- Password hashing and duplicate registration
- Product image validation and update behavior
- Successful inventory reduction and price calculation
- Unknown products and insufficient stock
- Product controller responses and structured errors
Start the API and run:
.\loadtest.ps1The script creates a temporary user, logs in for a JWT, warms up the API, and reports throughput, success/error rates, and p50/p95/p99 latency for concurrent product-list requests.
Before a public deployment, add versioned Flyway migrations, replace database image storage with object storage, introduce pagination, and configure a managed secret store. Hibernate ddl-auto=update is retained for local development convenience.