Skip to main content
The Rest Generic Class package implements a store-agnostic cache strategy using Laravel’s native cache system. This allows you to use Redis, database, file, Memcached, or any other Laravel-supported cache driver without code changes.

How Cache Works

Cache is applied in BaseService for read operations:
  • list_all - Caches paginated list responses
  • get_one - Caches individual resource responses
Write operations (create, update, destroy, destroybyid) automatically invalidate the cache by bumping a model-level cache version.

Cache Key Fingerprint

The cache key is built from multiple factors to ensure correct cache isolation:
FactorPurpose
Model classSeparate cache per resource type
OperationDifferent keys for list_all vs get_one
Route/pathDifferent endpoints get different cache
Query parametersDifferent filters/selects/relations get different cache
Authenticated userUser-specific data gets user-specific cache
Request headersMulti-tenant and locale-aware caching
Model cache versionAutomatic invalidation on writes
This prevents cache pollution where one query’s results might incorrectly serve another query.

Enabling Cache

1. Configure Environment

Set these variables in your .env file:
REST_CACHE_ENABLED=true
REST_CACHE_STORE=redis
REST_CACHE_TTL=60
Cache is disabled by default (REST_CACHE_ENABLED=false). You must explicitly enable it.

2. Select Cache Store

The REST_CACHE_STORE variable accepts any Laravel cache driver:
REST_CACHE_STORE=redis
The array store only persists cache for the current request lifecycle. Don’t use it in production.

3. Configure Store in Laravel

Ensure your selected cache store is properly configured in config/cache.php:
'stores' => [
    'redis' => [
        'driver' => 'redis',
        'connection' => 'cache',
        'lock_connection' => 'default',
    ],
    'database' => [
        'driver' => 'database',
        'table' => 'cache',
        'connection' => null,
        'lock_connection' => null,
    ],
    // ...
],
For database caching, run the migration:
php artisan cache:table
php artisan migrate

TTL Configuration

Default TTL

Set a baseline TTL for all cached responses:
REST_CACHE_TTL=60

Method-Specific TTLs

Override TTL for specific operations:
REST_CACHE_TTL_LIST=300    # 5 minutes for list endpoints
REST_CACHE_TTL_ONE=600     # 10 minutes for single-item reads
Configuration structure:
'cache' => [
    'ttl' => (int)env('REST_CACHE_TTL', 60),
    'ttl_by_method' => [
        'list_all' => (int)env('REST_CACHE_TTL_LIST', 60),
        'get_one' => (int)env('REST_CACHE_TTL_ONE', 30),
    ],
],

TTL Strategy Recommendations

Data TypeRecommended TTLReason
Frequently updated30-60 secondsBalance freshness and performance
Moderately updated5-15 minutesReduce database load significantly
Rarely updated30-60 minutesMaximize cache hit rate
Static/reference data1-24 hoursNear-permanent caching
List endpoints (list_all) often have shorter TTLs than single-item reads (get_one) because lists change more frequently when new items are added.

Per-Request Cache Control

Clients can control caching behavior on individual requests using query parameters.

Disable Cache for One Request

GET /api/v1/products?cache=false
This bypasses the cache entirely and always queries fresh data from the database.

Override TTL for One Request

GET /api/v1/products?cache_ttl=120
This caches the response for 120 seconds, overriding the configured TTL.

Combined Example

GET /api/v1/products?select=["id","name"]&cache_ttl=300
Cache this specific query shape for 5 minutes.
Per-request cache control is useful for debugging and special cases, but shouldn’t be relied upon for normal operation. Configure appropriate default TTLs instead.

Multi-Tenant and Locale Awareness

Vary by Headers

By default, cache keys vary by specific request headers to prevent cross-tenant or cross-locale data leaks:
'cache' => [
    'vary' => [
        'headers' => ['Accept-Language', 'X-Tenant-Id'],
    ],
],
How it works:
  • Request with Accept-Language: en gets a different cache key than Accept-Language: es
  • Request with X-Tenant-Id: tenant-a gets a different cache key than X-Tenant-Id: tenant-b

Customizing Vary Headers

If your application uses different headers for tenant identification, update the configuration:
'cache' => [
    'vary' => [
        'headers' => [
            'Accept-Language',
            'X-Organization-Id',  // Custom tenant header
            'X-Client-Version',    // Version-specific caching
        ],
    ],
],
Security critical: If your application is multi-tenant, ensure your tenant identification header is listed in cache.vary.headers. Otherwise, users may see cached data from other tenants.

Cache Invalidation

Automatic Version-Based Invalidation

The package uses a model-level cache versioning strategy:
  1. Each model has a version key stored in cache (e.g., cache_version:products)
  2. Every cache key includes the current model version
  3. On write operations (create, update, destroy), the version is bumped
  4. Existing cache keys become stale automatically without explicit deletion
Example flow:
1. GET /products → Cache key: products:v1:list:...
2. POST /products (create) → Bump version to v2
3. GET /products → Cache key: products:v2:list:... (cache miss, queries DB)

Manual Cache Clearing

Clear all application cache:
php artisan cache:clear
Clear cache for a specific store:
php artisan cache:clear --store=redis
You rarely need to manually clear cache because version-based invalidation handles writes automatically.

Cache Without Tags Support

The package’s version-based invalidation strategy works with all cache stores, including those that don’t support tags (like file, database, memcached). This is more flexible than tag-based invalidation, which only works with Redis and a few other stores.

Example Configurations

High-Traffic Production (Redis)

REST_CACHE_ENABLED=true
REST_CACHE_STORE=redis
REST_CACHE_TTL=300
REST_CACHE_TTL_LIST=300
REST_CACHE_TTL_ONE=600
Best for: APIs with heavy read traffic and moderate write traffic

Simple Staging (File)

REST_CACHE_ENABLED=true
REST_CACHE_STORE=file
REST_CACHE_TTL=120
REST_CACHE_TTL_LIST=120
REST_CACHE_TTL_ONE=180
Best for: Staging environments without Redis infrastructure

Distributed Applications (Database)

REST_CACHE_ENABLED=true
REST_CACHE_STORE=database
REST_CACHE_TTL=180
REST_CACHE_TTL_LIST=180
REST_CACHE_TTL_ONE=300
Best for: Multi-server setups sharing a database but without Redis

Development (Disabled)

REST_CACHE_ENABLED=false
Best for: Local development where you always want fresh data

Monitoring Cache Performance

Track these metrics to optimize your cache configuration:
MetricTargetAction if Below Target
Cache hit rate> 80%Increase TTL
Average response time< 100msEnable caching or increase TTL
Cache memory usage< 80% capacityReduce TTL or add more cache capacity
Database query countSignificant reductionCache is working well

Troubleshooting

Cache Not Working

Check that REST_CACHE_ENABLED=true in .env and configuration is not cached with old values.
php artisan config:clear
php artisan config:cache
Ensure the store specified in REST_CACHE_STORE exists in config/cache.php and is properly configured.For Redis, verify connection:
php artisan tinker
>>> Cache::store('redis')->get('test')
If using file cache, ensure storage/framework/cache is writable:
chmod -R 775 storage/framework/cache

Cache Serving Stale Data

If cache persists after updates, check:
  1. Version bump logic - Ensure write operations are using BaseService methods
  2. Custom write logic - If you bypass BaseService, manually bump the cache version
  3. TTL too long - Reduce TTL if data staleness is unacceptable

Environment Variables

Complete reference of cache-related environment variables

Validation Cache

Configure caching for validation queries

Build docs developers (and LLMs) love