Skip to content

andersonunsonst/catalog-api

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Catalog API - Laravel Product Management System

CI/CD Pipeline PHP Version Laravel Version License

A robust REST API built with Laravel 12 for managing a product catalog with advanced search capabilities, caching, and cloud storage integration.


📋 Table of Contents


🚀 Features

  • ✅ Complete CRUD operations for products
  • ✅ MySQL database with soft deletes
  • ✅ ElasticSearch integration for advanced search and filtering
  • ✅ Redis caching with intelligent invalidation
  • ✅ AWS S3 integration for product image uploads
  • ✅ Docker environment (app + MySQL + Redis + ElasticSearch)
  • ✅ Comprehensive test suite (Unit + Feature tests)
  • ✅ GitHub Actions CI/CD pipeline
  • ✅ Clean architecture with Repository and Service patterns
  • ✅ Automatic ElasticSearch synchronization via Observers

📋 Requirements

Minimum Requirements:

  • Docker 20.10+
  • Docker Compose 2.0+
  • Git

Optional (for local development without Docker):

  • PHP 8.2+
  • Composer 2.0+
  • MySQL 8.0+
  • Redis 7+
  • ElasticSearch 8.11+

🛠️ Tech Stack

Component Technology Version
Framework Laravel 12.x
Language PHP 8.2+
Database MySQL 8.0
Cache Redis 7.x
Search Engine ElasticSearch 8.11
Storage AWS S3 -
Web Server Nginx Alpine
Testing PHPUnit 11.x
CI/CD GitHub Actions -

🚀 Quick Start

1️⃣ Clone the repository

git clone <repository-url>
cd catalog-api

2️⃣ Configure environment

The .env file is already configured for Docker. For AWS S3, update:

AWS_ACCESS_KEY_ID=your-access-key-id
AWS_SECRET_ACCESS_KEY=your-secret-access-key
AWS_BUCKET=catalog-products
AWS_DEFAULT_REGION=us-east-1

Note: S3 is optional. The app will fallback to local storage if AWS credentials are not configured.

3️⃣ Start Docker containers

docker-compose up -d

Wait for services to be ready (~30 seconds)

4️⃣ Install dependencies

docker-compose exec app composer install

5️⃣ Run migrations

docker-compose exec app php artisan migrate

6️⃣ Seed database (optional)

docker-compose exec app php artisan db:seed

7️⃣ Create ElasticSearch index

docker-compose exec app php artisan elasticsearch:create-index
docker-compose exec app php artisan elasticsearch:reindex

✅ Verify Installation

curl http://localhost:8000/api/products

Expected response: {"data":[],"meta":{...}}


🎯 Alternative: One-Command Setup

./setup.sh

This script will:

  • ✅ Start Docker containers
  • ✅ Install dependencies
  • ✅ Run migrations
  • ✅ Seed database
  • ✅ Create ElasticSearch index
  • ✅ Verify installation

📡 API Endpoints

Products

Method Endpoint Description Auth
GET /api/products List all products (paginated) No
GET /api/products/{id} Get product by ID No
POST /api/products Create new product No
PUT /api/products/{id} Update product No
DELETE /api/products/{id} Delete product (soft delete) No
POST /api/products/{id}/image Upload product image to S3 No

Search

Method Endpoint Description Auth
GET /api/search/products Search products with filters No

Query Parameters

Pagination:

  • page (default: 1)
  • per_page (default: 15, max: 100)

Filters:

  • category (string)
  • status (active/inactive)
  • min_price (decimal)
  • max_price (decimal)

Sorting:

  • sort (price, created_at, name)
  • order (asc, desc)

Search:

  • q (search query)

🧪 Running Tests

Run All Tests

docker-compose exec app php artisan test

Or using Makefile:

make test

Run Specific Test Suites

# Unit tests only
make test-unit

# Feature tests only
make test-feature

# Specific test
make test-filter FILTER=test_can_create_product

# Verbose output
make test-verbose

Test Coverage

What's Tested:

  • ✅ Product CRUD operations
  • ✅ SKU uniqueness validation
  • ✅ Cache behavior (hit/miss/invalidation)
  • ✅ Search with filters (ElasticSearch)
  • ✅ Image upload (S3 + local fallback)
  • ✅ Error handling (404, 422, 400)
  • ✅ Soft deletes
  • ✅ Pagination
  • ✅ Repository pattern
  • ✅ Service layer

Expected Output

PASS  Tests\Unit\ProductServiceTest
✓ can create product
✓ can update product
✓ can delete product

PASS  Tests\Feature\ProductTest
✓ can create product
✓ cannot create product with duplicate sku
✓ can update product
✓ can delete product
✓ show nonexistent product returns 404
✓ product is cached after first request
✓ cache is cleared when product is updated

PASS  Tests\Feature\ProductSearchTest
✓ can search products
✓ can filter by category
✓ can filter by price range

Tests:  15 passed (25 assertions)
Duration: 2.34s

📬 API Testing

Option 1: Postman (Recommended)

  1. Import Collection

    # Open Postman
    # Click "Import" → "Upload Files"
    # Select: postman_collection.json
  2. Set Environment

    • The collection includes a baseUrl variable
    • Default: http://localhost:8000/api
  3. Run Requests

    • Expand folders: Products, Filters, Search, Error Cases
    • Click any request and hit "Send"

Download: https://www.postman.com/downloads/


Option 2: Insomnia

  1. Import Collection
    # Open Insomnia
    # Click "Import/Export" → "Import Data" → "From File"
    # Select: insomnia_collection.json

Download: https://insomnia.rest/download


Option 3: VS Code REST Client

  1. Install Extension: "REST Client" by Huachao Mao
  2. Open: api-requests.http
  3. Click: "Send Request" above each request

Option 4: cURL

# Create Product
curl -X POST http://localhost:8000/api/products \
  -H "Content-Type: application/json" \
  -d '{
    "sku": "LAPTOP-001",
    "name": "Gaming Laptop",
    "price": 1299.99,
    "category": "Electronics"
  }'

# Get All Products
curl http://localhost:8000/api/products?per_page=10&page=1

# Search Products
curl "http://localhost:8000/api/search/products?q=laptop&category=Electronics"

📦 Collection Contents

Total: 19 pre-configured requests

  • Products (6): Create, List, Get, Update, Delete, Upload Image
  • Filters (5): Category, Status, Price Range, Sort by Price, Sort by Date
  • Search (5): Simple, Multiple Filters, Pagination, Category Only, Complex
  • Error Cases (3): Validation Error, Duplicate SKU, Not Found

📝 Technical Decisions

Architecture Patterns

1. Repository Pattern

  • Why: Separates data access logic from business logic
  • Benefit: Easy to swap data sources (MySQL → MongoDB)
  • Location: app/Repositories/ProductRepository.php

2. Service Layer

  • Why: Encapsulates business rules and orchestrates operations
  • Benefit: Controllers stay thin, logic is reusable
  • Location: app/Services/ProductService.php

3. Observer Pattern

  • Why: Automatic ElasticSearch sync on model events
  • Benefit: No manual sync needed, always up-to-date
  • Location: app/Observers/ProductObserver.php

4. Form Request Validation

  • Why: Centralized validation logic
  • Benefit: Reusable, testable, clean controllers
  • Location: app/Http/Requests/

Caching Strategy

Redis Cache with Smart Invalidation:

// Cache individual products (120s TTL)
Cache::remember("product.{$id}", 120, fn() => Product::find($id));

// Cache product lists (120s TTL)
Cache::remember("products.page.{$page}", 120, fn() => Product::paginate());

// No cache for pages > 50 (avoid memory bloat)
if ($page > 50) {
    return Product::paginate($perPage);
}

// Invalidate on changes
Cache::forget("product.{$id}");
Cache::flush(); // For list caches

Why this approach:

  • ✅ Fast response times (cache hit ~5ms vs DB query ~50ms)
  • ✅ Automatic invalidation on updates
  • ✅ Memory-efficient (no deep pagination cache)

ElasticSearch Sync

Current: Synchronous via Observer

// ProductObserver.php
public function created(Product $product) {
    $this->elasticSearchService->indexProduct($product);
}

Why synchronous:

  • ✅ Simpler implementation
  • ✅ Immediate consistency
  • ✅ No queue infrastructure needed

Trade-off:

  • ❌ Slight delay on create/update (~50ms)
  • Mitigated: Queue job available but not enabled by default

Soft Deletes

Why soft deletes:

  • ✅ Data recovery possible
  • ✅ Audit trail maintained
  • ✅ Referential integrity preserved

Implementation:

// Product.php
use SoftDeletes;

// Automatically adds deleted_at column
// DELETE /api/products/1 → sets deleted_at = now()

AWS S3 Integration

Fallback Strategy:

// ImageUploadService.php
try {
    $path = Storage::disk('s3')->put('products', $image);
} catch (\Exception $e) {
    // Fallback to local storage
    $path = Storage::disk('public')->put('products', $image);
}

Why fallback:

  • ✅ Works without AWS credentials (development)
  • ✅ Graceful degradation
  • ✅ No deployment blockers

⚠️ Known Limitations

1. ElasticSearch Sync

Current State:

  • Synchronous via Observer
  • ~50ms delay on product create/update

Mitigation:

  • ✅ Queue job available (SyncProductToElasticSearch)
  • ✅ Can be enabled by uncommenting in ProductObserver

Impact: Minimal for typical workloads (<100 products/min)

Future Solution:

// Enable queue-based sync
dispatch(new SyncProductToElasticSearch($product));

2. Cache Invalidation

Current State:

  • Uses Cache::flush() for list caches
  • Clears ALL product-related caches on any change

Mitigation:

  • ✅ Only affects product caches (isolated Redis database)
  • ✅ Cache rebuilds automatically on next request

Impact: Temporary cache miss after product updates

Future Solution:

// Use cache tags (requires Redis 5.0+)
Cache::tags(['products'])->flush();

3. AWS S3 Credentials

Current State:

  • Requires real AWS credentials for S3 upload
  • Falls back to local storage if not configured

Mitigation:

  • ✅ Automatic fallback to storage/app/public
  • ✅ No errors if S3 unavailable

Impact: None for development

Future Solution:

  • Use LocalStack for local S3 emulation
  • No AWS credentials needed

4. Authentication

Current State:

  • No authentication/authorization implemented
  • API is publicly accessible

Impact: Not production-ready

Future Solution:

// Implement Laravel Sanctum
Route::middleware('auth:sanctum')->group(function () {
    Route::apiResource('products', ProductController::class);
});

5. Rate Limiting

Current State:

  • No rate limiting on API endpoints

Impact: Vulnerable to abuse/DDoS

Future Solution:

// Add throttle middleware
Route::middleware('throttle:60,1')->group(function () {
    Route::apiResource('products', ProductController::class);
});

6. Soft Delete Recovery

Current State:

  • No endpoint to restore soft-deleted products
  • Can only restore via database

Impact: Manual intervention required

Future Solution:

// Add restore endpoint
POST /api/products/{id}/restore

🔜 Next Steps

🔴 High Priority

  • Queue-based ElasticSearch Indexing

    • Move ES sync to background jobs
    • Improve API response times
    • Better error handling and retry logic
  • API Authentication

    • Implement Laravel Sanctum
    • Add user registration/login
    • Protect endpoints with middleware
  • Rate Limiting

    • Implement per-user rate limits
    • Add throttle middleware
    • Return proper 429 responses

🟡 Medium Priority

  • Cache Tags

    • Replace Cache::flush() with tagged caches
    • More granular cache invalidation
    • Better cache management
  • Comprehensive Logging

    • Add request/response logging middleware
    • Implement log rotation
    • Add performance metrics
  • API Versioning

    • Implement /api/v1/ prefix
    • Prepare for future API changes
    • Maintain backward compatibility
  • Soft Delete Recovery

    • Add restore endpoint
    • Add "trashed" filter to list endpoint
    • Add force delete endpoint (admin only)

🟢 Low Priority

  • LocalStack Integration

    • S3 emulation for local development
    • No AWS credentials needed
    • Faster development cycle
  • API Documentation

    • Generate OpenAPI/Swagger docs
    • Interactive API explorer
    • Auto-generated from code
  • Performance Monitoring

    • Add Laravel Telescope
    • Monitor slow queries
    • Track cache hit rates
  • Database Indexing

    • Add composite indexes for common queries
    • Optimize search performance
    • Analyze query patterns

🎯 Production Readiness Checklist

Before deploying to production:

Security

  • Enable authentication (Laravel Sanctum)
  • Add rate limiting
  • Configure CORS properly
  • Set up SSL/TLS
  • Sanitize error messages (hide stack traces)

Performance

  • Enable queue-based ES indexing
  • Use cache tags
  • Configure database indexes
  • Set up CDN for static assets
  • Configure auto-scaling

Monitoring

  • Set up monitoring (New Relic, Datadog, etc.)
  • Add error tracking (Sentry, Bugsnag, etc.)
  • Configure proper logging
  • Add health check endpoint
  • Set up uptime monitoring

Infrastructure

  • Use environment-specific .env files
  • Set up database backups
  • Configure CI/CD pipeline
  • Implement graceful shutdown
  • Set up staging environment

Documentation

  • API documentation (OpenAPI/Swagger)
  • Deployment guide
  • Runbook for common issues
  • Architecture diagrams

🛠️ Useful Commands

# Start containers
docker-compose up -d

# Stop containers
docker-compose down

# View logs
docker-compose logs -f app

# Access shell
docker-compose exec app bash

# Restart services
docker-compose restart
# Run migrations
docker-compose exec app php artisan migrate

# Seed database
docker-compose exec app php artisan db:seed

# Clear cache
docker-compose exec app php artisan cache:clear

# Run tests
docker-compose exec app php artisan test

# Access Tinker
docker-compose exec app php artisan tinker
make install      # Complete setup
make up           # Start containers
make down         # Stop containers
make test         # Run tests
make shell        # Access app shell
make logs         # View logs
make cache-clear  # Clear all caches

📚 Additional Resources


📄 License

This project is open-sourced software licensed under the MIT license.


👤 Author

Your Name


🙏 Acknowledgments

  • Laravel Framework
  • ElasticSearch
  • Redis
  • AWS S3
  • Docker

Made with ❤️ using Laravel 12

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors