> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/charlietyn/rest-generic-class/llms.txt
> Use this file to discover all available pages before exploring further.

# Cache Configuration and Usage

> Configure Redis, database, or file-based caching for optimal API performance

Rest Generic Class includes a comprehensive caching layer that works with any Laravel cache store. This guide shows you how to configure caching, understand cache key generation, and manage cache invalidation.

## Cache Architecture

The caching system is designed to:

1. **Support any Laravel cache store** (Redis, database, file, Memcached, DynamoDB)
2. **Cache read operations** (`list_all`, `get_one`) automatically
3. **Invalidate cache on writes** using versioned keys
4. **Vary by request context** (user, tenant, locale, query params)
5. **Allow per-request control** with `cache` and `cache_ttl` parameters

<Note>
  Cache is **disabled by default**. You must explicitly enable it in configuration.
</Note>

## Quick Start

<Steps>
  ### Enable Cache

  Set environment variables:

  ```env .env theme={null}
  REST_CACHE_ENABLED=true
  REST_CACHE_STORE=redis
  REST_CACHE_TTL=60
  REST_CACHE_TTL_LIST=60
  REST_CACHE_TTL_ONE=30
  ```

  ### Configure Cache Store

  Make sure your chosen cache store is configured in `config/cache.php`:

  ```php config/cache.php theme={null}
  'stores' => [
      'redis' => [
          'driver' => 'redis',
          'connection' => 'cache',
          'lock_connection' => 'default',
      ],
  ],
  ```

  ### Test Cache

  Make two identical requests and verify the second is faster:

  ```bash theme={null}
  # First request (cache miss)
  curl -X GET "http://api.example.com/api/v1/products" \
    -H "Content-Type: application/json"

  # Second request (cache hit)
  curl -X GET "http://api.example.com/api/v1/products" \
    -H "Content-Type: application/json"
  ```

  ### Monitor Cache

  Check Redis keys (if using Redis):

  ```bash theme={null}
  redis-cli KEYS "rgc:v1:*"
  ```
</Steps>

## Configuration Reference

```php config/rest-generic-class.php theme={null}
'cache' => [
    // Enable/disable cache globally
    'enabled' => env('REST_CACHE_ENABLED', false),
    
    // Laravel cache store name (redis, database, file, memcached, etc.)
    'store' => env('REST_CACHE_STORE', env('CACHE_STORE')),
    
    // Default TTL in seconds
    'ttl' => (int)env('REST_CACHE_TTL', 60),
    
    // Method-specific TTL overrides
    'ttl_by_method' => [
        'list_all' => (int)env('REST_CACHE_TTL_LIST', 60),
        'get_one' => (int)env('REST_CACHE_TTL_ONE', 30),
    ],
    
    // Methods that will be cached
    'cacheable_methods' => ['list_all', 'get_one'],
    
    // Headers to include in cache key (for multi-tenancy/localization)
    'vary' => [
        'headers' => ['Accept-Language', 'X-Tenant-Id'],
    ],
],
```

### Environment Variables

| Variable              | Default       | Description                     |
| --------------------- | ------------- | ------------------------------- |
| `REST_CACHE_ENABLED`  | `false`       | Master switch for package cache |
| `REST_CACHE_STORE`    | `CACHE_STORE` | Laravel store name              |
| `REST_CACHE_TTL`      | `60`          | Default TTL (seconds)           |
| `REST_CACHE_TTL_LIST` | `60`          | TTL for list endpoints          |
| `REST_CACHE_TTL_ONE`  | `30`          | TTL for single-record endpoints |

## Cache Store Options

### Redis (Recommended for Production)

Best for:

* High-traffic APIs
* Distributed applications (multiple servers)
* Sub-millisecond cache hits

```env .env theme={null}
REST_CACHE_ENABLED=true
REST_CACHE_STORE=redis
REST_CACHE_TTL=300
```

**Setup Redis:**

```bash theme={null}
# Install Redis
sudo apt-get install redis-server

# Install PHP Redis extension
pecl install redis

# Install Laravel Redis package
composer require predis/predis
```

### Database

Best for:

* Simple deployments
* Shared hosting without Redis
* When you need queryable cache data

```env .env theme={null}
REST_CACHE_ENABLED=true
REST_CACHE_STORE=database
REST_CACHE_TTL=180
```

**Create cache table:**

```bash theme={null}
php artisan cache:table
php artisan migrate
```

### File

Best for:

* Development
* Small applications
* Single-server deployments

```env .env theme={null}
REST_CACHE_ENABLED=true
REST_CACHE_STORE=file
REST_CACHE_TTL=120
```

<Warning>
  File cache doesn't work in multi-server deployments. Each server has its own cache, leading to inconsistencies.
</Warning>

### Memcached

Best for:

* Legacy systems already using Memcached
* High-performance in-memory caching

```env .env theme={null}
REST_CACHE_ENABLED=true
REST_CACHE_STORE=memcached
REST_CACHE_TTL=300
```

## How Cache Keys Work

Cache keys are generated from a fingerprint that includes:

1. **Operation** (`list_all` or `get_one`)
2. **Model class** (e.g., `App\Models\Product`)
3. **Route** (name or path)
4. **HTTP method** (GET, POST, etc.)
5. **Query parameters** (`select`, `relations`, `oper`, `pagination`, etc.)
6. **Vary headers** (`Accept-Language`, `X-Tenant-Id`)
7. **Authenticated user ID**
8. **Request parameters** (from request body)
9. **Model cache version** (for invalidation)

Example fingerprint:

```php theme={null}
[
    'op' => 'list_all',
    'model' => 'App\\Models\\Product',
    'route' => 'api.v1.products.index',
    'method' => 'GET',
    'query' => ['select' => ['id','name'], 'relations' => ['category']],
    'headers' => ['Accept-Language' => 'en', 'X-Tenant-Id' => null],
    'user' => 42,
    'params' => ['oper' => ['and' => ['status|=|active']]],
    'version' => 5,
]
```

This is hashed to create the cache key:

```
rgc:v1:a3f7b2e1c9d4f6a8e2b5c7d9f1a3e5b7
```

<Note>
  **Any change** in the fingerprint creates a different cache key. This ensures correct cache isolation.
</Note>

## Multi-Tenancy Support

The `vary.headers` configuration prevents cache pollution across tenants or locales.

### Example: Multi-Tenant SaaS

**Configuration:**

```php config/rest-generic-class.php theme={null}
'cache' => [
    'vary' => [
        'headers' => ['X-Tenant-Id'],
    ],
],
```

**Requests:**

```bash theme={null}
# Tenant A request
curl -H "X-Tenant-Id: tenant-a" \
     "http://api.example.com/api/v1/products"

# Tenant B request (different cache key)
curl -H "X-Tenant-Id: tenant-b" \
     "http://api.example.com/api/v1/products"
```

Each tenant gets a separate cache entry, preventing data leaks.

### Example: Multi-Language API

**Configuration:**

```php config/rest-generic-class.php theme={null}
'cache' => [
    'vary' => [
        'headers' => ['Accept-Language'],
    ],
],
```

**Requests:**

```bash theme={null}
# English request
curl -H "Accept-Language: en" \
     "http://api.example.com/api/v1/products"

# Spanish request (different cache key)
curl -H "Accept-Language: es" \
     "http://api.example.com/api/v1/products"
```

## Cache Invalidation

Rest Generic Class uses **versioned keys** for automatic invalidation.

### How It Works

1. Each model has a version number stored in cache
2. Version is included in every read cache key
3. On write operations (`create`, `update`, `destroy`), version is bumped
4. Old cache keys become unreachable (effectively invalidated)

**Example:**

```php theme={null}
// Initial state
Product cache version: 1
Cache key: rgc:v1:...{version:1}...abc123

// Update product
PUT /api/v1/products/10

// After update
Product cache version: 2 (bumped)
Old cache key: rgc:v1:...{version:1}...abc123 (orphaned)
New cache key: rgc:v1:...{version:2}...def456 (fresh)
```

<Note>
  This approach works across **all cache stores**, including those without tag support (file, database).
</Note>

### Version Storage

Versions are stored with keys like:

```
rgc:v1:version:App\Models\Product
```

Versions are stored **forever** (no TTL), ensuring consistent invalidation.

### Manual Invalidation

To manually clear cache for a model:

```php theme={null}
$service = new ProductService();

// Call the private bumpCacheVersion() method via reflection
$reflection = new \ReflectionClass($service);
$method = $reflection->getMethod('bumpCacheVersion');
$method->setAccessible(true);
$method->invoke($service);
```

Or clear all cache:

```bash theme={null}
php artisan cache:clear
```

## Per-Request Cache Control

Override cache behavior on a per-request basis.

### Disable Cache for One Request

```http theme={null}
GET /api/v1/products?cache=false
```

This bypasses cache and queries the database directly (but doesn't update cache).

### Custom TTL for One Request

```http theme={null}
GET /api/v1/products?cache_ttl=300
```

This request will be cached for 300 seconds (5 minutes) instead of the default.

### Example: Fresh Data on Demand

For admin users who need real-time data:

```http theme={null}
GET /api/v1/products?cache=false
Authorization: Bearer admin_token
```

For public users (cached):

```http theme={null}
GET /api/v1/products
Authorization: Bearer user_token
```

## Performance Impact

### Benchmark Results

**Test:** List 100 products with category relation

| Cache Store | First Request (Miss) | Second Request (Hit) | Improvement      |
| ----------- | -------------------- | -------------------- | ---------------- |
| No cache    | 45ms                 | 45ms                 | -                |
| Redis       | 45ms                 | 2ms                  | **22.5x faster** |
| Database    | 45ms                 | 8ms                  | **5.6x faster**  |
| File        | 45ms                 | 5ms                  | **9x faster**    |

### When to Use Cache

✅ **Enable cache for:**

* Read-heavy endpoints (product listings, category trees)
* Data that changes infrequently (settings, configurations)
* High-traffic public APIs
* Expensive queries (complex filters, multiple relations)

❌ **Disable cache for:**

* Write-heavy endpoints (webhooks, real-time updates)
* User-specific data (shopping carts, notifications)
* Admin dashboards requiring fresh data
* Development/testing environments

## Monitoring Cache

### Redis Monitoring

Check cache hit rate:

```bash theme={null}
redis-cli INFO stats | grep keyspace
```

List all package keys:

```bash theme={null}
redis-cli KEYS "rgc:v1:*" | wc -l
```

Monitor cache operations in real-time:

```bash theme={null}
redis-cli MONITOR
```

### Laravel Telescope

Enable cache monitoring in Telescope:

```php config/telescope.php theme={null}
'watchers' => [
    Watchers\CacheWatcher::class => true,
],
```

View cache operations at `/telescope/cache`.

### Custom Logging

Log cache hits/misses:

```php theme={null}
// In your service override
public function list_all($params, $toJson = true): mixed
{
    $cacheKey = $this->buildCacheKey('list_all', $params);
    $cached = Cache::has($cacheKey);
    
    Log::info('Cache ' . ($cached ? 'HIT' : 'MISS'), [
        'model' => static::class,
        'key' => $cacheKey,
    ]);
    
    return parent::list_all($params, $toJson);
}
```

## Troubleshooting

### Cache Not Working

**Symptom:** Identical requests are always slow

**Checks:**

1. Is cache enabled? `REST_CACHE_ENABLED=true`
2. Is the store configured? Check `config/cache.php`
3. Is the method cacheable? Check `cacheable_methods` config
4. Are you testing with `cache=false`?

**Debugging:**

```bash theme={null}
# Check cache status
php artisan tinker
>>> config('rest-generic-class.cache.enabled')
=> true

# Test cache store
>>> Cache::store('redis')->put('test', 'value', 60);
>>> Cache::store('redis')->get('test');
=> "value"
```

### Stale Data After Updates

**Symptom:** GET returns old data after PUT/DELETE

**Cause:** Cache version not bumped (transaction rollback?)

**Solution:** Check logs for transaction errors:

```bash theme={null}
tail -f storage/logs/rest-generic-class.log
```

Manually bump version if needed (see Manual Invalidation above).

### Cache Growing Too Large

**Symptom:** Cache store running out of memory

**Causes:**

* TTL too high
* Too many unique requests (different query params)
* Version keys accumulating

**Solutions:**

* Lower TTL: `REST_CACHE_TTL=30`
* Use Redis maxmemory policy: `maxmemory-policy allkeys-lru`
* Periodically clear old versions:

```bash theme={null}
redis-cli --scan --pattern "rgc:v1:version:*" | xargs redis-cli DEL
```

### Different Cache per Server

**Symptom:** Inconsistent responses in load-balanced setup

**Cause:** Using file cache with multiple servers

**Solution:** Switch to Redis or Memcached:

```env .env theme={null}
REST_CACHE_STORE=redis
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Advanced Filtering" icon="filter" href="/guides/advanced-filtering">
    Optimize cached queries with efficient filters
  </Card>

  <Card title="Bulk Operations" icon="layer-group" href="/guides/bulk-operations">
    Understand cache invalidation with bulk updates
  </Card>

  <Card title="Performance Tuning" icon="gauge-high" href="/performance/optimization">
    Advanced performance optimization techniques
  </Card>

  <Card title="Configuration Reference" icon="gear" href="/configuration/cache">
    Complete cache configuration options
  </Card>
</CardGroup>

## Evidence

* **File:** `src/Core/Services/BaseService.php`\
  **Lines:** 1082-1212\
  Implements `shouldUseCache()`, `rememberWithCache()`, `buildCacheKey()`, `resolveCacheTtl()`, `getCacheVersion()`, `bumpCacheVersion()`

* **File:** `config/rest-generic-class.php`\
  **Lines:** 37-52\
  Defines cache configuration structure

* **File:** `documentacion/doc-en/02-configuration/03-cache-strategy.md`\
  **Lines:** 1-66\
  Explains cache strategy and store selection
