Backend REST API built with Spring Boot 3. Covers authentication, product catalogue, categories, shopping cart, and order management.

| Layer |
Technology |
| Runtime |
Java 17 |
| Framework |
Spring Boot 3.5 |
| Database |
MySQL 8.4 + Flyway |
| Cache / Token store |
Redis 7.4 |
| Auth |
JWT (access + refresh tokens) |
| Docs |
SpringDoc OpenAPI 3 |
| Email (dev) |
MailHog |
| Build |
Gradle (Kotlin DSL) |
- Java 17+
- Docker & Docker Compose
# 1. Start infrastructure
docker compose up -d
# 2. Run the app (default profile reads localhost env vars)
./gradlew bootRun
The API is available at http://localhost:8080.
Swagger UI: http://localhost:8080/swagger-ui.html
MailHog UI (email preview): http://localhost:8025
All variables have defaults that work with the Docker Compose setup out of the box.
| Variable |
Default |
Description |
DB_URL |
jdbc:mysql://localhost:3306/osiris |
JDBC connection URL |
DB_USERNAME |
root |
Database user |
DB_PASSWORD |
(empty) |
Database password |
REDIS_HOST |
localhost |
Redis host |
REDIS_PORT |
6379 |
Redis port |
JWT_SECRET |
(dev key) |
Base64-encoded secret — change in production |
JWT_EXPIRATION |
3600000 |
Access token TTL in ms (1 hour) |
MAIL_HOST |
localhost |
SMTP host |
MAIL_PORT |
1025 |
SMTP port |
MAIL_FROM |
noreply@osiris.dev |
Sender address |
APP_BASE_URL |
http://localhost:8080 |
Used in email links |
CORS_ALLOWED_ORIGINS |
http://localhost:3000,http://localhost:5173 |
Comma-separated allowed origins |
| Method |
Endpoint |
Auth |
Description |
| POST |
/auth/register |
Public |
Register a new user |
| POST |
/auth/login |
Public |
Login, returns access + refresh tokens |
| POST |
/auth/refresh |
Public |
Rotate refresh token |
| POST |
/auth/logout |
Bearer |
Invalidate current session |
| POST |
/auth/logout-all |
Bearer |
Invalidate all sessions for the user |
| POST |
/auth/forgot-password |
Public |
Send password reset email |
| POST |
/auth/reset-password |
Public |
Reset password with token |
| GET |
/auth/verify-email |
Public |
Verify email address with token |
| Method |
Endpoint |
Auth |
Description |
| GET |
/users/me |
Bearer |
Get current user profile |
| PUT |
/users/me |
Bearer |
Update current user profile |
| DELETE |
/users/me |
Bearer |
Delete account |
| Method |
Endpoint |
Auth |
Description |
| GET |
/categories |
Bearer |
List all categories (paginated) |
| GET |
/categories/{id} |
Bearer |
Get category by ID |
| POST |
/categories |
Admin |
Create category |
| PUT |
/categories/{id} |
Admin |
Update category |
| DELETE |
/categories/{id} |
Admin |
Delete category |
| Method |
Endpoint |
Auth |
Description |
| GET |
/products |
Bearer |
List products (paginated, supports ?search=) |
| GET |
/products/{id} |
Bearer |
Get product by ID |
| POST |
/products |
Admin |
Create product |
| PUT |
/products/{id} |
Admin |
Update product |
| DELETE |
/products/{id} |
Admin |
Delete product |
| Method |
Endpoint |
Auth |
Description |
| GET |
/cart |
Bearer |
Get current user's cart with total |
| POST |
/cart/items |
Bearer |
Add item (merges quantity if product already in cart) |
| PUT |
/cart/items/{itemId} |
Bearer |
Update item quantity |
| DELETE |
/cart/items/{itemId} |
Bearer |
Remove item |
| DELETE |
/cart |
Bearer |
Clear cart |
| Method |
Endpoint |
Auth |
Description |
| POST |
/orders/checkout |
Bearer |
Create order from cart (validates stock, deducts inventory) |
| GET |
/orders |
Bearer |
List my orders (paginated, newest first) |
| GET |
/orders/{id} |
Bearer |
Get order detail |
| DELETE |
/orders/{id}/cancel |
Bearer |
Cancel order (only if status is PENDING) |
| Method |
Endpoint |
Auth |
Description |
| GET |
/admin/users |
Admin |
List all users (paginated) |
| PUT |
/admin/users/{id}/role |
Admin |
Change user role |
| GET |
/admin/orders |
Admin |
List all orders (paginated, newest first) |
| PUT |
/admin/orders/{id}/status |
Admin |
Update order status |
Order statuses: PENDING → PROCESSING → SHIPPED → DELIVERED / CANCELLED
POST /auth/register → { accessToken, refreshToken }
POST /auth/login → { accessToken, refreshToken }
Authorization: Bearer <accessToken> (on every protected request)
POST /auth/refresh → { accessToken, refreshToken } (rotate before expiry)
POST /auth/logout → 204 (blacklists access token)
Access tokens are short-lived (1 h by default). Refresh tokens are stored in the database and can be revoked individually or in bulk via /auth/logout-all.
Flyway runs automatically on startup. Migration files live in src/main/resources/db/migration/.
| Version |
Description |
| V1 |
users, refresh_tokens, products |
| V2 |
categories table + image_url column on products |
| V3 |
carts, cart_items, orders, order_items |
Tests use an H2 in-memory database and MockMvc. No external services required.
src/main/java/dev/alexissdev/osiris/
├── auth/ # JWT, refresh tokens, email verification, password reset
├── user/ # User entity, profile management
├── admin/ # Admin-only user and role management
├── category/ # Product categories
├── product/ # Product catalogue
├── cart/ # Shopping cart
├── order/ # Order lifecycle
├── config/ # OpenAPI, cache, MDC logging filter
└── exception/ # Global exception handler