⚠️ Disclaimer: This is a showcase / portfolio project, not a production-ready system. It may evolve toward production use in the future, but as of now it hasn't been hardened, audited, or tested for that purpose. Use it at your own risk — no responsibility is taken for bugs, security issues, or data loss resulting from its use.
RESTful task management API built with Java and Spring Boot. Backend showcase project focused on authentication, authorization, REST API design, persistence, validation, testing, and clean separation of responsibilities.
- User registration and authentication (JWT access + refresh tokens)
- Refresh token rotation with reuse prevention
- Refresh tokens stored as SHA-256 hashes (never in plaintext)
- Logout with refresh token revocation
- Role based authorization
- Project, task, and comment management
- Task status and priority management, task assignment
- Pagination and sorting
- Request validation and global exception handling
- PostgreSQL persistence
- Integration tests with Testcontainers
- Docker support
Java 25 · Spring Boot · Spring Security · Spring Data JPA · Hibernate · PostgreSQL · Gradle · JUnit 5 · MockMvc · Testcontainers · Docker
Layered architecture with domain based package organization (controller → service → repository), separating entities (persistent domain objects) from DTOs (API contract).
src/main/java/dev/iamforyy/taskmanagementapi/
├── auth/ AuthController, AuthService, JwtService
├── user/ User, UserController, UserService, UserRepository
├── project/ Project, ProjectController, ProjectService, ProjectRepository
├── task/ Task, TaskController, TaskService, TaskRepository
└── comment/ Comment, CommentController, CommentService, CommentRepository
Two-token model:
- Access token — short lived, authenticates API requests
- Refresh token — long lived, used to obtain a new access token; stored as a SHA-256 hash
Refresh flow (rotation): validate existing token → revoke it → issue new access + refresh tokens → store new hash → return both to the client. This prevents a consumed refresh token from being reused.
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| POST | /auth/register |
Register a new user | Public |
| POST | /auth/login |
Authenticate a user | Public |
| POST | /auth/refresh |
Refresh access and refresh tokens | Public |
| POST | /auth/logout |
Revoke a refresh token | Public |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| GET | /api/users/me |
Get the currently authenticated user | Required |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| GET | /api/projects |
Get projects | Required |
| GET | /api/projects/{projectId} |
Get a project by ID | Required |
| POST | /api/projects |
Create a project | Required |
| PATCH | /api/projects/{projectId} |
Update a project | Required |
| DELETE | /api/projects/{projectId} |
Delete a project | Required |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| GET | /api/tasks |
Get tasks | Required |
| GET | /api/tasks/{taskId} |
Get a task by ID | Required |
| POST | /api/tasks |
Create a task | Required |
| PATCH | /api/tasks/{taskId} |
Update a task | Required |
| PATCH | /api/tasks/{taskId}/status |
Update task status | Required |
| DELETE | /api/tasks/{taskId} |
Delete a task | Required |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| GET | /api/tasks/{taskId}/comments |
Get task comments | Required |
| POST | /api/tasks/{taskId}/comments |
Create a comment | Required |
| PATCH | /api/comments/{commentId} |
Update a comment | Required |
| DELETE | /api/comments/{commentId} |
Delete a comment | Required |
POST /auth/register
Content-Type: application/json
{ "username": "john", "email": "john@example.com", "password": "password123" }POST /auth/login
Content-Type: application/json
{ "email": "john@example.com", "password": "password123" }Response:
{ "accessToken": "eyJ...", "refreshToken": "..." }Authenticated request:
GET /api/users/me
Authorization: Bearer eyJ...Refresh (returns a new access + refresh token pair; previous refresh token is revoked):
POST /auth/refresh
Content-Type: application/json
{ "refreshToken": "..." }Logout (revokes the refresh token):
POST /auth/logout
Content-Type: application/json
{ "refreshToken": "..." }Collection endpoints support pagination and sorting via Spring Data's Pageable:
GET /api/projects?page=0&size=20&sort=name,ascRequest DTOs use Jakarta Bean Validation. Invalid requests, auth errors, missing resources, and invalid/expired tokens are handled by a global exception handler with a consistent error shape:
{
"status": 404,
"code": "RESOURCE_NOT_FOUND",
"message": "Task not found",
"timestamp": "2026-08-25T12:00:00Z"
}User
├── Project
│ └── Task
│ └── Comment
└── RefreshToken
PostgreSQL, mapped via JPA. Passwords are hashed with Spring Security's PasswordEncoder; refresh tokens are stored as SHA-256 hashes. No sensitive data is ever stored in plaintext.
Integration tests with JUnit 5, Spring Boot Test, MockMvc, and Testcontainers, covering auth requirements, token refresh/logout, registration, validation, response status/content, and persistence.
./gradlew test # gradlew.bat test on WindowsRequirements: Java 25, Docker, PostgreSQL, Git
git clone <repository-url>
cd task-management-apiSet the required environment variables (e.g. via .env — never commit real secrets):
POSTGRES_DB=tmapdb
POSTGRES_USER=tmap
POSTGRES_PASSWORD=your_postgres_password
JWT_SECRET=your_jwt_secret
JWT_EXPIRATION=300000
Run:
./gradlew bootRun # gradlew.bat bootRun on WindowsAPI available at http://localhost:8080.
./gradlew build
docker build -t task-management-api .
docker run -p 8080:8080 task-management-apiOr run the full stack (API + PostgreSQL) with Docker Compose:
docker compose upThis project demonstrates production-oriented backend development practices, including:
- Authentication & authorization with Spring Security (JWT access + rotating refresh tokens, role-based authorization)
- Persistence with Spring Data JPA + Hibernate and relational data modeling
- Layered architecture with clear domain/DTO separation
- Request validation and centralized error handling
- Integration testing with Testcontainers
- Containerization for reproducible deployment
- OpenAPI / Swagger documentation
- Rate limiting
- Redis caching
- Better refresh token reuse detection
- Email verification & password reset
- Account management
- Advanced task filtering
- Project-scoped membership - restrict project visibility to the creator and users explicitly added by the project owner
- Per-project roles/permissions (e.g. owner vs. member, beyond the current global role check)
- Audit logging
- CI/CD improvements & production deployment
MIT License.