Simple REST API built with Spring Boot for managing categories and products.
This project exposes CRUD endpoints for:
- Categories
- Products
The API includes:
- Input validation
- Centralized error handling
- Pagination for list endpoints
- OpenAPI/Swagger documentation
- Flyway migrations for MySQL schema and seed data
- Java 17
- Spring Boot 3.5.11
- Spring Web
- Spring Data JPA
- Bean Validation (Jakarta Validation)
- Flyway
- MySQL 8 (Docker compose available)
- Maven Wrapper (
mvnw)
src/main/java/com/thiiagoms/gsales
application/ # use cases
domain/ # entities, value objects, domain rules
infrastructure/ # persistence, framework adapters
presentation/ # REST controllers, requests/responses, exception handling
src/main/resources
application.properties
db/migration # Flyway SQL migrations
- JDK 17+
- Docker + Docker Compose
Optional:
- Maven (not required if using
./mvnw)
- Create your local environment file:
cp .env.example .env- Review and adjust credentials in
.env.
- Start MySQL container:
docker compose up -d mysql- Run database migrations:
./mvnw flyway:migrate- Start the API:
./mvnw spring-boot:runApplication default URL:
http://localhost:8080
- Create your environment file:
cp .env.example .env- Build and start all services:
docker compose up -d --build- Check containers:
docker compose ps- Stop services:
docker compose downWhen running with Docker Compose:
- API:
http://localhost:8080 - MySQL:
localhost:3306(or the value fromDB_PORT)
Notes:
- The API container points to the MySQL service using
mysqlas host. - Flyway migrations run automatically on API startup.
When the app is running:
- Swagger UI:
http://localhost:8080/swagger-ui/index.html - OpenAPI JSON:
http://localhost:8080/v3/api-docs
GET /api/v1/categories?page=0&size=10GET /api/v1/categories/{id}POST /api/v1/categoriesPUT /api/v1/categories/{id}DELETE /api/v1/categories/{id}
Create/update payload:
{
"description": "Electronics"
}GET /api/v1/products?page=0&size=10GET /api/v1/products/{id}POST /api/v1/productsPUT /api/v1/products/{id}DELETE /api/v1/products/{id}
Create payload:
{
"categoryId": "a1b2c3d4-e5f6-47a8-b9c0-d1e2f3a4b5c6",
"description": "Gaming Notebook",
"quantity": 12,
"costPrice": 1499.90,
"salePrice": 1999.90,
"observation": "Last unit in stock"
}Update payload:
{
"description": "Gaming Notebook Pro",
"quantity": 8,
"costPrice": 1550.00,
"salePrice": 2199.90,
"observation": "New batch arriving soon"
}{
"content": [],
"page": 0,
"size": 10,
"totalElements": 0,
"totalPages": 0
}{
"timestamp": "2026-03-18T12:00:00Z",
"status": 400,
"error": "Bad Request",
"message": "Description is required.",
"path": "/api/v1/categories"
}Flyway migrations create tables and insert initial data:
V01__create_and_register_categories.sqlV02__create_and_register_products.sql
You can use the seeded category ID below to create products quickly:
a1b2c3d4-e5f6-47a8-b9c0-d1e2f3a4b5c6
Run tests:
./mvnw testBuild artifact:
./mvnw clean packageFormat code (Spotless):
./mvnw spotless:applyRun static analysis plugins:
./mvnw checkstyle:check
./mvnw pmd:check
./mvnw spotbugs:check- The default profile uses MySQL through
application.properties. - In tests, Flyway is disabled by
application-test.properties. allowPublicKeyRetrieval=truein JDBC URL is intended for local development.