Skip to content

Repository files navigation

TaskFlow API

A RESTful task management API built with Spring Boot 4 and Java 17, designed as a portfolio project demonstrating production-ready backend development practices.


Tech Stack

Category Technology
Language Java 17
Framework Spring Boot 4.0.6 (Spring Framework 7)
Build Tool Gradle (Groovy DSL)
Database PostgreSQL
Migrations Liquibase
ORM Spring Data JPA + Hibernate 7
Security Spring Security 7 + JWT (jjwt 0.12.5)
Mapping MapStruct 1.6.3
Boilerplate Lombok
API Docs SpringDoc OpenAPI 3.0.2
Testing JUnit 5 + Mockito + AssertJ + Testcontainers
Containerization Docker + Docker Compose

Features

  • JWT Authentication — Stateless auth with Bearer tokens, BCrypt password hashing
  • Project Management — Create and manage projects with ownership-based access control
  • Task Tracking — Full task lifecycle with status, priority, and assignee support
  • Comments — Task-level commenting system
  • Input Validation — Bean Validation on all request DTOs
  • Structured Error Responses — Global exception handling via @RestControllerAdvice
  • Database Migrations — Versioned schema management with Liquibase
  • API Documentation — Auto-generated Swagger UI via SpringDoc OpenAPI

Architecture

The project follows a classic Layered Architecture with strict separation of concerns:

HTTP Request
    │
    ▼
JwtAuthenticationFilter       ← validates JWT, sets SecurityContext
    │
    ▼
Spring Security FilterChain   ← authorization rules
    │
    ▼
Controller                    ← HTTP mapping, @Valid, @AuthenticationPrincipal
    │
    ▼
Service                       ← business logic, @Transactional
    │
    ▼
Repository                    ← Spring Data JPA
    │
    ▼
PostgreSQL

Key design decisions:

  • Controllers never return Entity objects directly — always Response DTOs
  • MapStruct handles all Entity ↔ DTO conversions at compile time (zero reflection overhead)
  • Business-level authorization (owner checks) lives in the Service layer, not Security config
  • All database schema changes are versioned Liquibase changesets — no ddl-auto: update

Project Structure

src/main/java/com/taskflow/
├── config/             # SecurityConfig, OpenApiConfig
├── controller/         # AuthController, ProjectController, TaskController, CommentController
├── dto/
│   ├── request/        # RegisterRequest, LoginRequest, ProjectRequest, TaskRequest...
│   └── response/       # AuthResponse, ProjectResponse, TaskResponse...
├── entity/             # User, Project, Task, Comment
├── enums/              # Role, TaskStatus, TaskPriority
├── exception/          # ResourceNotFoundException, AccessDeniedException, DuplicateResourceException
├── mapper/             # ProjectMapper, TaskMapper, CommentMapper (MapStruct)
├── repository/         # UserRepository, ProjectRepository, TaskRepository, CommentRepository
├── security/           # JwtService, JwtAuthenticationFilter, UserDetailsImpl, UserDetailsServiceImpl
└── service/
    └── impl/           # AuthServiceImpl, ProjectServiceImpl, TaskServiceImpl, CommentServiceImpl

API Endpoints

Auth

Method Endpoint Access Description
POST /api/v1/auth/register Public Register a new user
POST /api/v1/auth/login Public Authenticate and get JWT token

Projects

Method Endpoint Access Description
POST /api/v1/projects Authenticated Create a project
GET /api/v1/projects Authenticated Get all projects for current user
GET /api/v1/projects/{id} Owner only Get project by ID
PUT /api/v1/projects/{id} Owner only Update project
DELETE /api/v1/projects/{id} Owner only Delete project

Tasks

Method Endpoint Access Description
POST /api/v1/projects/{projectId}/tasks Project owner Create a task
GET /api/v1/projects/{projectId}/tasks Project member Get all tasks
GET /api/v1/projects/{projectId}/tasks/{id} Project member Get task by ID
PUT /api/v1/projects/{projectId}/tasks/{id} Project member Update task
DELETE /api/v1/projects/{projectId}/tasks/{id} Project owner Delete task

Comments

Method Endpoint Access Description
POST /api/v1/tasks/{taskId}/comments Authenticated Add a comment
GET /api/v1/tasks/{taskId}/comments Authenticated Get comments for a task
DELETE /api/v1/tasks/{taskId}/comments/{id} Comment author Delete a comment

Getting Started

Prerequisites

  • Docker + Docker Compose

Run with Docker Compose

git clone https://github.com/chizhiks/taskflow-api.git
cd taskflow-api

docker compose up --build

One command builds the app image, starts PostgreSQL, waits for it to become healthy, then starts the API. No local Java, Gradle, or PostgreSQL installation needed.

The API will be available at http://localhost:8080 Swagger UI: http://localhost:8080/swagger-ui.html

Run Tests

Make sure Docker Desktop is running — integration tests use Testcontainers to spin up a real PostgreSQL instance.

./gradlew test

Configuration

Key properties in src/main/resources/application.yml:

spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/taskflow
  jpa:
    hibernate:
      ddl-auto: validate   # Schema managed by Liquibase, not Hibernate
    open-in-view: false

app:
  jwt:
    secret: ${JWT_SECRET}
    expiration-ms: 86400000  # 24 hours

Testing

The project includes three types of tests:

Unit Tests (@ExtendWith(MockitoExtension.class))

  • AuthServiceTest — 4 tests covering register and login scenarios
  • ProjectServiceTest — 6 tests covering CRUD and access control

Integration Tests (@SpringBootTest + Testcontainers)

  • AuthControllerIntegrationTest — 4 tests covering full HTTP request lifecycle with real PostgreSQL

Context Test

  • TaskFlowApplicationTests — verifies the Spring context loads correctly

All tests follow the Given-When-Then pattern with AssertJ assertions.


Database Schema

users
├── id (PK)
├── username (UNIQUE)
├── email (UNIQUE)
├── password (BCrypt)
├── first_name
├── last_name
└── role

projects
├── id (PK)
├── name
├── description
└── owner_id (FK → users)

tasks
├── id (PK)
├── title
├── description
├── status (TODO, IN_PROGRESS, DONE)
├── priority (LOW, MEDIUM, HIGH)
├── project_id (FK → projects)
└── assignee_id (FK → users)

comments
├── id (PK)
├── content
├── task_id (FK → tasks)
└── author_id (FK → users)

Schema is managed by 4 Liquibase changesets in src/main/resources/db/changelog/changes/.


Security

  • Passwords hashed with BCrypt (adaptive cost factor, automatic salting)
  • Authentication via JWT Bearer tokens — fully stateless, no server-side sessions
  • Authorization checks at two levels:
    • Spring Security SecurityFilterChain — endpoint-level (authenticated vs public)
    • Service layer — resource-level (owner/member verification)

Author

Andrii Chyzhov

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages