REST API for managing fruit stock with providers using MySQL database.
- Description
- Requirements
- Technologies
- Project Structure
- Setup & Installation
- Running the Application
- API Endpoints
- Testing
- Docker
- Database Schema
- Development Process
- Assignment Details
This project is a Spring Boot REST API for managing a fruit inventory system with providers. It allows you to:
- Manage Providers: Create, read, update, and delete fruit suppliers
- Manage Fruits: Track fruit stock with associated providers
- Filter by Provider: Query fruits supplied by specific providers
- Full CRUD Operations: Complete Create, Read, Update, Delete functionality
The application follows best practices including:
- β TDD (Test-Driven Development) Outside-In approach
- β Clean Architecture with MVC pattern
- β DTOs and validation with Bean Validation
- β Global exception handling
- β Database relationships with JPA
- β Docker containerization
- β Environment variable configuration
- Register new providers with name and country
- No duplicate provider names allowed
- List all registered providers
- Update provider information
- Delete providers (only if they have no associated fruits)
- Add fruits with name, weight (kg), and provider
- All fruits must have an associated provider
- Filter fruits by provider
- List all fruits
- Get fruit details by ID
- Update fruit information (including changing provider)
- Delete fruits
- Proper HTTP status codes (200, 201, 204, 400, 404, 409)
- Input validation with error messages
- Global exception handling
- RESTful API design
- MySQL database in production
- H2 in-memory database for testing
- Comprehensive test coverage (79+ tests)
- Java 21 (LTS)
- Spring Boot 3.4.1
- Spring Web (REST Controllers)
- Spring Data JPA (ORM)
- Hibernate (JPA Implementation)
- MySQL 8.0 (Production)
- H2 Database (Testing)
- Bean Validation (Jakarta Validation)
- Lombok (Reduce boilerplate)
- JUnit 5 (Test framework)
- Mockito (Mocking)
- MockMvc (Controller testing)
- AssertJ (Assertions)
- Docker (Containerization)
- Docker Compose (Multi-container orchestration)
- Maven (Build tool)
fruit-api-mysql/
βββ src/
β βββ main/
β β βββ java/cat/itacademy/s04/t02/n02/fruit/
β β β βββ controller/
β β β β βββ FruitController.java
β β β β βββ ProviderController.java
β β β βββ service/
β β β β βββ FruitService.java
β β β β βββ FruitServiceImpl.java
β β β β βββ ProviderService.java
β β β β βββ ProviderServiceImpl.java
β β β βββ repository/
β β β β βββ FruitRepository.java
β β β β βββ ProviderRepository.java
β β β βββ model/
β β β β βββ Fruit.java
β β β β βββ Provider.java
β β β βββ dto/
β β β β βββ FruitRequestDTO.java
β β β β βββ FruitResponseDTO.java
β β β β βββ ProviderRequestDTO.java
β β β β βββ ProviderResponseDTO.java
β β β βββ mapper/
β β β β βββ FruitMapper.java
β β β β βββ ProviderMapper.java
β β β βββ exception/
β β β β βββ ResourceNotFoundException.java
β β β β βββ DuplicateResourceException.java
β β β β βββ ResourceConflictException.java
β β β β βββ ErrorResponse.java
β β β β βββ GlobalExceptionHandler.java
β β β βββ FruitApiMysqlApplication.java
β β βββ resources/
β β βββ application.properties
β β βββ application-prod.properties
β βββ test/
β βββ java/cat/itacademy/s04/t02/n02/fruit/
β β βββ controller/
β β β βββ FruitControllerTest.java
β β β βββ ProviderControllerTest.java
β β βββ service/
β β β βββ FruitServiceTest.java
β β β βββ ProviderServiceTest.java
β β βββ integration/
β β β βββ FruitIntegrationTest.java
β β β βββ ProviderIntegrationTest.java
β β βββ FruitApiMysqlApplicationTests.java
β βββ resources/
β βββ application-test.properties
βββ .mvn/
βββ Dockerfile
βββ docker-compose.yml
βββ .dockerignore
βββ .env.example
βββ .gitignore
βββ pom.xml
βββ mvnw
βββ mvnw.cmd
βββ README.md
- Java 21 or higher
- Maven 3.8+ (or use included Maven Wrapper)
- Docker & Docker Compose
- Git
git clone <repository-url>
cd fruit-api-mysqlCopy the .env.example file to .env:
cp .env.example .envEdit .env with your credentials:
MYSQL_ROOT_PASSWORD=rootpassword
MYSQL_DATABASE=fruitdb
MYSQL_USER=fruituser
MYSQL_PASSWORD=fruitpass
DB_URL=jdbc:mysql://localhost:3306/fruitdb
DB_USERNAME=fruituser
DB_PASSWORD=fruitpassdocker-compose up -d mysqlVerify MySQL is running:
docker-compose ps./mvnw spring-boot:runThe application will start on http://localhost:8080
Build and start all services:
docker-compose up --build -dView logs:
docker-compose logs -f appStop services:
docker-compose down./mvnw clean package -DskipTests
java -jar target/fruit-api-mysql-0.0.1-SNAPSHOT.jarGET /actuator/healthResponse: 200 OK
{
"status": "UP"
}POST /providers
Content-Type: application/json
{
"name": "Fruits Inc",
"country": "Spain"
}Response: 201 Created
{
"id": 1,
"name": "Fruits Inc",
"country": "Spain"
}GET /providersResponse: 200 OK
[
{
"id": 1,
"name": "Fruits Inc",
"country": "Spain"
}
]GET /providers/{id}Response: 200 OK or 404 Not Found
PUT /providers/{id}
Content-Type: application/json
{
"name": "Updated Fruits Inc",
"country": "Italy"
}Response: 200 OK or 404 Not Found or 409 Conflict
DELETE /providers/{id}Response: 204 No Content or 404 Not Found or 409 Conflict
POST /fruits
Content-Type: application/json
{
"name": "Apple",
"weightInKilos": 10,
"providerId": 1
}Response: 201 Created
{
"id": 1,
"name": "Apple",
"weightInKilos": 10,
"provider": {
"id": 1,
"name": "Fruits Inc",
"country": "Spain"
}
}GET /fruits/allResponse: 200 OK
GET /fruits?providerId=1Response: 200 OK or 404 Not Found
GET /fruits/{id}Response: 200 OK or 404 Not Found
PUT /fruits/{id}
Content-Type: application/json
{
"name": "Updated Apple",
"weightInKilos": 15,
"providerId": 2
}Response: 200 OK or 404 Not Found
DELETE /fruits/{id}Response: 204 No Content or 404 Not Found
./mvnw test./mvnw clean test jacoco:reportView coverage report:
open target/site/jacoco/index.html- Total Tests: 91
- Controller Tests: 36
- Service Unit Tests: 26
- Integration Tests: 33
- Coverage: >90%
- Unit Tests: Mockito for isolated testing
- Controller Tests: MockMvc with @WebMvcTest
- Integration Tests: @SpringBootTest with H2 database
docker build -t fruit-api-mysql:latest .The Dockerfile uses a multi-stage build for optimization:
- Build Stage: Compiles the application with Maven (JDK 21)
- Runtime Stage: Runs the application with minimal JRE 21
Benefits:
- Smaller image size (~250MB vs ~600MB)
- Better security (no build tools in production)
- Faster deployment
services:
mysql: # MySQL 8.0 database
app: # Spring Boot applicationNetworks: fruit-network (bridge)
Volumes: mysql_data (persistent storage)
# Start services
docker-compose up -d
# View logs
docker-compose logs -f app
# Stop services
docker-compose down
# Remove volumes (β οΈ deletes data)
docker-compose down -v
# Rebuild images
docker-compose up --buildCREATE TABLE providers (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255) NOT NULL UNIQUE,
country VARCHAR(255) NOT NULL
);CREATE TABLE fruits (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255) NOT NULL,
weight_in_kilos INT NOT NULL,
provider_id BIGINT NOT NULL,
FOREIGN KEY (provider_id) REFERENCES providers(id)
);- Provider β Fruit: One-to-Many (1:N)
- Fruit β Provider: Many-to-One (N:1)
- Cascade delete: Deleting a provider with fruits returns
409 Conflict
For each feature, we followed this cycle:
- RED: Write failing acceptance test (Controller)
- GREEN: Implement minimal code to pass (Controller + mocked Service)
- RED: Write failing unit test (Service)
- GREEN: Implement Service logic
- REFACTOR: Clean up code
- INTEGRATION: Write end-to-end test
- COMMIT: Save working feature
Controller Test (MockMvc) β
β
Controller Implementation β
β
Service Test (Mockito) β
β
Service Implementation β
β
Repository & Entity
β
Integration Test (@SpringBootTest) β
β
Commit π
- Course: IT Academy - Spring Framework
- Sprint: S4 - API REST with Spring Boot
- Level: Level 2 - MySQL Integration
- Group: cat.itacademy.s04.t02.n02
- Artifact: fruit-api-mysql
β
Create REST APIs with Spring Boot
β
Persist data with Spring Data JPA
β
Apply HTTP verbs and status codes correctly
β
Implement dynamic routes with Path and Query Params
β
Write automated tests with TDD
β
Handle exceptions globally with @ControllerAdvice
β
Structure projects with MVC pattern
β
Create entity relationships with JPA
β
Use DTOs and validate input data
β
Containerize applications with Docker
β
Configure databases with environment variables
- β Register a new provider
- β List all providers
- β Update provider information
- β Delete a provider
- β Add fruit with provider
- β Filter fruits by provider
- β List all fruits
- β Get specific fruit by ID
- β Update fruit information
- β Delete a fruit