CloudNest is a cloud-native file storage and user management backend built with Spring Boot, designed to demonstrate enterprise-grade backend engineering. It features a complete authentication system backed by JWT tokens — including secure token issuance, expiration handling, refresh token rotation, and logout — protected by a custom Spring Security filter chain. Users can upload, retrieve, and delete documents stored in MinIO (AWS S3-compatible), with pre-signed URLs providing temporary, authenticated access to private files.
The system uses PostgreSQL as its primary database with Liquibase handling schema migrations, ensuring version-controlled, reproducible database states across environments. Infrastructure is fully Dockerized, enabling seamless local development and straightforward cloud deployment. Role-based access control separates public, user-level, and admin-level endpoints, with a global exception handler ensuring consistent, meaningful error responses throughout the API.
| Technology | Purpose |
|---|---|
| Java 21 | Language |
| Spring Boot 4.x | Framework |
| Spring Security + JWT | Authentication & Authorization |
| PostgreSQL | Database |
| Liquibase | Database migrations |
| MinIO (S3-compatible) | File storage |
| Docker | Containerization |
| Lombok | Boilerplate reduction |
src/
├── config/ # Security, JWT, S3, App configuration
├── controller/ # REST controllers
├── dao/ # JPA entities
├── dto/ # Request/Response DTOs
├── exception/ # Custom exceptions & global handler
├── repository/ # Spring Data JPA repositories
└── service/ # Business logic
- Java 21+
- Docker & Docker Compose
- PostgreSQL (or use Docker)
git clone https://github.com/your-username/cloudnest.git
cd cloudnestdocker-compose up -dThis starts MinIO at:
- API:
http://localhost:9000 - Dashboard:
http://localhost:9001
Login to the dashboard with your S3_ACCESS_KEY and S3_SECRET_KEY to manage buckets. The bucket is auto-created on app startup.
./gradlew bootRunThe API will be available at http://localhost:8080.
services:
postgresql:
image: postgres:16
container_name: CloudNest_db
restart: always
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
minio:
image: quay.io/minio/minio
container_name: minio
ports:
- "9000:9000"
- "9001:9001"
environment:
MINIO_ROOT_USER: ${S3_ACCESS_KEY}
MINIO_ROOT_PASSWORD: ${S3_SECRET_KEY}
volumes:
- minio_data:/data
command: server /data --console-address ":9001"
volumes:
postgres_data:
minio_data:CloudNest uses JWT Bearer Token authentication.
POST /api/public/users → Register
PUT /api/public/users/login → Login → returns accessToken + refreshToken
PUT /api/users/refresh-token → Refresh tokens (Authorization: Bearer <refreshToken>)
PUT /api/users/logout → Logout (Authorization: Bearer <refreshToken>)
Add the Authorization header to all protected requests:
Authorization: Bearer <accessToken>
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/public/users |
Register a new user |
PUT |
/api/public/users/login |
Login and get tokens |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
GET |
/api/users/{id} |
Get user by ID | ✅ |
PUT |
/api/users/{id} |
Update user | ✅ |
DELETE |
/api/users/{id} |
Delete user | ✅ |
PUT |
/api/users/logout |
Logout | ✅ |
PUT |
/api/users/refresh-token |
Refresh access token | ✅ |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
POST |
/api/drivers/upload/{userId} |
Upload a driver document | ✅ |
GET |
/api/drivers/user/{userId} |
Get all documents for a user | ✅ |
GET |
/api/drivers/{id} |
Get document by ID | ✅ |
GET |
/api/drivers/{id}/url |
Get file download URL | ✅ |
DELETE |
/api/drivers/{id} |
Delete document | ✅ |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
GET |
/api/admin/users |
Get all users | ✅ ADMIN |
GET |
/api/admin/drivers |
Get all driver documents | ✅ ADMIN |
curl -X POST http://localhost:8080/api/drivers/upload/{userId} \
-H "Authorization: Bearer <token>" \
-F "file=@/path/to/document.pdf"Response:
{
"id": "uuid",
"userId": "uuid",
"fileName": "document.pdf",
"fileType": null,
"createdAt": "2026-03-29T00:00:00",
"updatedAt": "2026-03-29T00:00:00"
}To access the file, call GET /api/drivers/{id}/url and open the returned URL in your browser.
CloudNest uses Liquibase for schema management. Migrations run automatically on startup.
Tables:
users— user accountsrefresh_tokens— active refresh tokensdrivers— driver document metadata
| Role | Access |
|---|---|
USER |
Standard endpoints |
ADMIN |
All endpoints including /api/admin/** |
Default role on registration is USER.
- JWT tokens expire in 24 hours (access) and 7 days (refresh)
- Files are stored in MinIO under the
cloudnest-bucketbucket - MinIO bucket is set to public for direct file access via
http://localhost:9000/cloudnest-bucket/<file-key>