A REST API for task management with soft delete functionality and advanced filtering capabilities.
- CRUD operations for tasks with priority levels (1-10) and categories
- Partial updates with DTOs: update only the fields you need to change
- Soft delete: tasks are marked as deleted rather than removed from the database
- Undo delete functionality by re-calling delete on a soft-deleted task
- Multi-dimensional filtering: combine category and priority range filters
- Automatic timestamp tracking for creation, completion, and deletion
- Toggle task completion status with timestamp recording
- Java 21
- Spring Boot 3.5.6
- Spring Data JPA
- H2 Database (file-based persistence)
- Gradle
- Java 21 or higher
- Gradle (or use included wrapper)
./gradlew bootRunThe API will start on http://localhost:8081
Access the H2 database console at http://localhost:8081/h2-console
- JDBC URL:
jdbc:h2:file:./data/demo - Username:
sa - Password: (leave blank)
GET /api/tasks
Query parameters (all optional):
category- filter by categoryhigher- minimum priority (inclusive)less- maximum priority (inclusive)
Examples:
/api/tasks- all active tasks/api/tasks?category=work- all work tasks/api/tasks?higher=5&less=8- tasks with priority 5-8/api/tasks?category=personal&higher=7- personal tasks with priority >= 7
GET /api/tasks/{id}
Returns 404 if task doesn't exist or is deleted.
POST /api/tasks
Content-Type: application/json
{
"name": "Task name",
"category": "work",
"priority": 5
}
PATCH /api/tasks/{id}
Content-Type: application/json
{
"name": "Updated task name",
"category": "personal",
"priority": 8
}
Partially update a task. All fields are optional - only provide the fields you want to change.
Examples:
- Update only priority:
{"priority": 9} - Update name and category:
{"name": "New name", "category": "urgent"} - Update all fields:
{"name": "Complete task", "category": "work", "priority": 10}
Returns 404 if task doesn't exist or is deleted.
PUT /api/tasks/toggle/{id}
Toggles completion status and sets/clears completedAt timestamp. Returns 400 if task is deleted.
DELETE /api/tasks/{id}
Marks task as deleted. Calling delete again on a deleted task will restore it (undo).
Task
id- Auto-generated primary keyname- Task name (required, not blank)category- Category label (optional)priority- Priority level 1-10 (required)completed- Boolean completion statusmadeAt- Creation timestamp (auto-set)completedAt- Completion timestamp (set on toggle)deletedAt- Deletion timestamp (null = active)
Standard three-layer Spring Boot architecture with DTOs and centralized exception handling:
- Controller - HTTP request handling and response mapping
- DTO (Data Transfer Objects) - API contract layer for request/response data
- Service - Business logic and transaction management
- Repository - Data access with Spring Data JPA
- Entity - Domain models mapped to database tables
- Exception - Custom exceptions with global exception handler
All query methods filter out soft-deleted tasks (deletedAt IS NULL) except for admin recovery endpoints.
Tasks are never removed from the database. The deletedAt timestamp marks a task as deleted:
null= active task (shown in normal queries)timestamp= deleted task (filtered from normal queries)
Admin methods (findTasksIncludingDeleted, findTaskByIdIncludingDeleted) bypass the filter for recovery scenarios.
The API uses Data Transfer Objects (DTOs) for request/response handling:
- TaskUpdateDTO - Accepts partial updates with optional fields
- DeleteResponseDTO - Structured response for delete/restore operations
- Only non-null fields in the DTO are applied to the entity
- Validation constraints apply only to provided fields
- Prevents mass assignment vulnerabilities
- Decouples API contract from database schema
Write operations use @Transactional to ensure atomicity:
- Creating tasks
- Updating tasks (partial or full)
- Toggling completion
- Soft delete/restore
Read operations don't require transactions as they don't modify data.
Centralized exception handling using @RestControllerAdvice:
Custom Exceptions:
- TaskNotFound - Thrown when task doesn't exist or is deleted (404 Not Found)
- TaskWasDeleted - Thrown when operations attempted on deleted tasks (400 Bad Request)
Global Exception Handler:
- Catches and formats all exceptions into consistent JSON responses
- Includes HTTP status code, error message, and timestamp
- Handles validation errors from Jakarta Bean Validation
- Returns first validation error message for cleaner responses
Response Format:
{
"status": 404,
"message": "Task not found or has been deleted",
"timestamp": "2025-10-22T10:30:00"
}Multi-layer validation strategy:
Entity-level Validation:
@NotBlankon name field@NotNullon priority field@Min(1)and@Max(10)on priority field- Custom error messages for better UX
Controller-level Validation:
@Validannotation triggers validation on request bodies- Applied to POST (create) and PATCH (update) endpoints
- Validation errors caught by GlobalExceptionHandler
- Returns 406 Not Acceptable with validation message
- Implementing soft delete patterns with recovery functionality
- Complex Spring Data JPA query method naming conventions
- Proper service layer transaction boundaries
- Multi-parameter conditional query routing in controllers
- RESTful API design with idempotent operations
- DTO pattern for flexible partial updates with validation
- Custom exception hierarchy and centralized error handling with @RestControllerAdvice
- Multi-layer validation strategy with Jakarta Bean Validation
- Moving business logic from controllers to service layer for better separation of concerns