Competency-Based Education Benchmark Platform API built with Spring Boot 3.4, Java 17, PostgreSQL, and Keycloak.
- Spring Boot 3.4.1 - Application framework
- Java 17 - Programming language
- PostgreSQL 18.1 - Database
- Keycloak 26.4 - Authentication & Authorization (OAuth2/JWT)
- Flyway - Database migrations
- Spring Data JPA - Data access
- Lombok - Boilerplate reduction
- SpringDoc OpenAPI - API documentation
- Gradle 8.11.1 - Build tool
- Java 17 JDK
- Docker & Docker Compose
- Gradle 8.11.1 (or use the wrapper)
docker-compose up -dThis starts:
- PostgreSQL (port 5433)
- Keycloak (port 8081)
- Spring Boot API (port 8080)
- API: http://localhost:8080
- Swagger UI: http://localhost:8080/swagger-ui.html
- Keycloak Admin: http://localhost:8081 (admin/admin)
- API Docs: http://localhost:8080/api-docs
The Keycloak realm comes with pre-configured users:
- Demo User: demo@memo.local / demo (USER role)
- Admin User: admin@memo.local / admin (ADMIN role)
- Start PostgreSQL and Keycloak:
docker-compose up postgres keycloak-postgres keycloak -d- Run the Spring Boot application:
./gradlew bootRun./gradlew build./gradlew test./gradlew spotlessApply./gradlew checkstyleMainAll endpoints require JWT authentication (except Swagger UI and health checks).
POST /api/users- Create userGET /api/users/{id}- Get user by IDGET /api/users/by-email?email=- Get user by emailGET /api/users- Get all usersPUT /api/users/{id}- Update userDELETE /api/users/{id}- Delete user
POST /api/competencies- Create competencyGET /api/competencies/{id}- Get competency by IDGET /api/competencies- Get all competenciesGET /api/competencies/random?count=2- Get random competenciesPUT /api/competencies/{id}- Update competencyDELETE /api/competencies/{id}- Delete competency
POST /api/learning-resources- Create learning resourceGET /api/learning-resources/{id}- Get resource by IDGET /api/learning-resources/by-url?url=- Get resource by URLGET /api/learning-resources- Get all resourcesPUT /api/learning-resources/{id}- Update resourceDELETE /api/learning-resources/{id}- Delete resource
POST /api/competency-relationships- Create relationshipGET /api/competency-relationships/{id}- Get relationship by IDGET /api/competency-relationships- Get all relationshipsDELETE /api/competency-relationships/{id}- Delete relationship
POST /api/competency-resource-links- Create linkGET /api/competency-resource-links/{id}- Get link by IDGET /api/competency-resource-links- Get all linksDELETE /api/competency-resource-links/{id}- Delete link
The API uses OAuth2/JWT via Keycloak. To authenticate:
- Obtain a token from Keycloak:
curl -X POST http://localhost:8081/realms/memo/protocol/openid-connect/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=memo-client" \
-d "username=demo@memo.local" \
-d "password=demo" \
-d "grant_type=password"- Use the
access_tokenin API requests:
curl -H "Authorization: Bearer YOUR_TOKEN" http://localhost:8080/api/competencies- Migrations: Located in
src/main/resources/db/migration - Auto-apply: Flyway runs migrations on startup
- Connection: PostgreSQL on port 5433
Environment variables (see .env.example):
DB_HOST=localhost
DB_PORT=5433
DB_NAME=memo
DB_USER=postgres
DB_PASSWORD=memo
KEYCLOAK_ISSUER_URI=http://localhost:8081/realms/memo
KEYCLOAK_JWK_SET_URI=http://localhost:8081/realms/memo/protocol/openid-connect/certs
SERVER_PORT=8080server/
├── src/main/java/de/tum/cit/memo/
│ ├── config/ # Configuration classes
│ ├── controller/ # REST controllers
│ ├── dto/ # Request/Response objects
│ ├── entity/ # JPA entities
│ ├── enums/ # Enumerations
│ ├── exception/ # Exception handling
│ ├── repository/ # Data access layer
│ ├── security/ # Security configuration
│ ├── service/ # Business logic
│ └── util/ # Utility classes
├── src/main/resources/
│ ├── db/migration/ # Flyway SQL scripts
│ └── application.yml # Application configuration
├── docker/
│ └── keycloak/ # Keycloak realm configuration
├── Dockerfile # Container image
└── docker-compose.yml # Multi-container setup
If ports 5433, 8080, or 8081 are already in use, stop the conflicting services or modify the ports
in docker-compose.yml.
Keycloak takes 30-40 seconds to fully start. Check logs:
docker-compose logs keycloakIf migrations fail, check Flyway status:
docker-compose exec server ./gradlew flywayInfo