Enterprise-style task management REST API built with Java 21 and Spring Boot 3.
A backend portfolio project focused on clean API design, authentication, persistence, schema migrations, automated testing and delivery hygiene.
Task Manager API is a REST API for personal task management.
It includes:
- JWT-based authentication.
- Per-user data isolation.
- Task creation, update, deletion and status management.
- Pagination, filtering and sorting.
- PostgreSQL persistence.
- Flyway database migrations.
- Docker and Docker Compose support.
- Automated tests with JUnit 5, Mockito, MockMvc and H2.
- CI pipeline with GitHub Actions.
- Swagger UI through SpringDoc OpenAPI.
The project is designed to demonstrate backend engineering practices commonly expected in enterprise environments: layered architecture, API contracts, validation, structured error handling, testability and repeatable local setup.
| Method | Endpoint | Auth | Description |
|---|---|---|---|
POST |
/api/auth/register |
Public | Register a new user |
POST |
/api/auth/login |
Public | Login and receive a JWT |
GET |
/api/tasks |
JWT | List tasks with pagination, filtering and sorting |
POST |
/api/tasks |
JWT | Create a task |
GET |
/api/tasks/{id} |
JWT | Get task by ID |
PUT |
/api/tasks/{id} |
JWT | Full task update |
PATCH |
/api/tasks/{id}/status |
JWT | Update task status only |
DELETE |
/api/tasks/{id} |
JWT | Delete task |
GET /api/tasks?status=TODO&priority=HIGH&page=0&size=20&sort=createdAt,desc| Layer | Technology |
|---|---|
| Language | Java 21 |
| Framework | Spring Boot 3.2 |
| Security | Spring Security + JJWT |
| Persistence | Spring Data JPA + PostgreSQL |
| Migrations | Flyway |
| Validation | Jakarta Bean Validation |
| API Documentation | SpringDoc OpenAPI / Swagger UI |
| Testing | JUnit 5, Mockito, MockMvc, H2 |
| Build Tool | Maven |
| Containers | Docker, Docker Compose |
| CI | GitHub Actions |
The project follows a layered architecture:
┌───────────────────────────────────────────────┐
│ Controller Layer │
│ HTTP requests, validation, responses │
└───────────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────┐
│ Service Layer │
│ Business logic and use cases │
└───────────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────┐
│ Repository Layer │
│ Spring Data JPA persistence │
└───────────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────┐
│ Database Layer │
│ PostgreSQL / H2 tests │
└───────────────────────────────────────────────┘
This separation keeps the HTTP layer thin, business rules testable and persistence concerns isolated.
Clone the repository:
git clone https://github.com/MPrazeres-1983/task-manager-api.git
cd task-manager-apiStart the application and database:
docker compose up --buildThe API will be available at:
http://localhost:8080
Swagger UI:
http://localhost:8080/swagger-ui.html
The Docker Compose setup provides local development defaults, including a JWT secret for local use only.
Clone the repository:
git clone https://github.com/MPrazeres-1983/task-manager-api.git
cd task-manager-apiCreate the required environment variables:
export DB_URL=jdbc:postgresql://localhost:5432/taskmanager
export DB_USER=postgres
export DB_PASS=postgres
export JWT_SECRET=ReplaceThisWithARealSecretAtLeast32BytesLong
export JWT_EXPIRATION_MS=86400000Run the application:
mvn spring-boot:run| Variable | Required | Description |
|---|---|---|
DB_URL |
yes | PostgreSQL JDBC connection URL |
DB_USER |
yes | Database username |
DB_PASS |
yes | Database password |
JWT_SECRET |
yes | Secret used to sign JWT tokens |
JWT_EXPIRATION_MS |
yes | JWT expiration time in milliseconds |
Example:
DB_URL=jdbc:postgresql://localhost:5432/taskmanager
DB_USER=postgres
DB_PASS=postgres
JWT_SECRET=ReplaceThisWithARealSecretAtLeast32BytesLong
JWT_EXPIRATION_MS=86400000- Register a user through
/api/auth/register. - Login through
/api/auth/login. - Copy the JWT from the login response.
- Send authenticated requests with:
Authorization: Bearer <token>Protected endpoints return 401 Unauthorized when the token is missing, invalid or expired.
When the application is running locally, Swagger UI is available at:
http://localhost:8080/swagger-ui.html
OpenAPI JSON:
http://localhost:8080/v3/api-docs
Run the full verification lifecycle:
mvn clean verifyThis runs:
- Unit tests.
- Integration tests.
- JaCoCo coverage report generation.
Useful outputs:
target/surefire-reports/
target/site/jacoco/index.html
The test suite is designed to validate the main risk areas of the API:
- Authentication and authorization.
- User data isolation.
- Task creation and update rules.
- Filtering, pagination and sorting.
- Validation errors.
- Structured error responses.
- Repository and service behaviour.
- Controller-level behaviour through MockMvc.
H2 is used for tests to keep the suite fast, repeatable and independent from the local PostgreSQL instance.
The API uses structured error handling based on Spring's ProblemDetail.
This makes API errors more predictable and easier to consume by clients.
Example categories:
400 Bad Request— invalid request body or validation failure.401 Unauthorized— missing or invalid authentication.403 Forbidden— authenticated user lacks permission.404 Not Found— requested resource does not exist.409 Conflict— duplicate or conflicting state.
src/
├── main/java/com/mprazeres/taskmanager/
│ ├── config/ # Security and OpenAPI configuration
│ ├── controller/ # HTTP layer
│ ├── dto/ # Request and response records
│ ├── exception/ # ProblemDetail-based error handling
│ ├── model/ # JPA entities and enums
│ ├── repository/ # Spring Data JPA repositories
│ ├── security/ # JWT services, filter and auth entry point
│ └── service/ # Business logic
├── main/resources/
│ ├── application.yml
│ └── db/migration/ # Flyway migrations
└── test/ # Unit and integration tests
The project uses schema migrations instead of relying on Hibernate automatic schema updates. This makes database evolution explicit, versioned and closer to real-world backend workflows.
Emails are trimmed and normalised to lowercase before authentication and persistence. This avoids duplicate-account edge cases caused by casing or accidental whitespace.
Protected endpoints return 401 Unauthorized with a structured problem response instead of relying on vague default framework behaviour.
Each user can only access their own tasks. This is one of the most important security and data integrity rules in the API.
- Add a public demo deployment.
- Add a refresh-token flow or token revocation strategy.
- Add a coverage badge.
- Add richer task search capabilities.
- Add request/response examples to the Swagger documentation.
- Add rate limiting for authentication endpoints.
- Add audit fields for task updates.
Mário Prazeres
- GitHub: github.com/MPrazeres-1983
- LinkedIn: linkedin.com/in/mario-prazeres
MIT — see the LICENSE file.