csa/guzzle-cache-middleware
PSR-7/PSR-18 middleware adding HTTP response caching to Guzzle clients. Cache GET/HEAD requests, reduce repeated network calls, and plug into common cache storage backends. Install via Composer and integrate in your Guzzle handler stack.
Installation
composer require csa/guzzle-cache-middleware
Register the middleware in your Guzzle client stack:
use CSA\GuzzleCacheMiddleware\CacheMiddleware;
use GuzzleHttp\HandlerStack;
$stack = HandlerStack::create();
$stack->push(CacheMiddleware::cacheMiddleware(
new \CSA\GuzzleCacheMiddleware\Cache\Psr6Cache(), // PSR-6 cache adapter
['ttl' => 3600] // Optional config
));
First Use Case Cache API responses for a Laravel HTTP client:
$client = new \GuzzleHttp\Client(['handler' => $stack]);
$response = $client->get('https://api.example.com/data');
// Subsequent identical requests will return cached responses.
Middleware Integration
Inject the middleware into Guzzle’s HandlerStack for all requests:
$stack->push(CacheMiddleware::cacheMiddleware($cacheAdapter, [
'ttl' => 3600,
'cache_key' => fn($request) => $request->getUri()->__toString(), // Custom key generator
]));
Cache Adapter Flexibility
Use any PSR-6 compatible cache (e.g., League\Flysystem\Cache\FileCache, Doctrine\Cache\Psr6Cache):
$cache = new \Doctrine\Cache\Psr6Cache(new \Doctrine\Common\Cache\FilesystemCache('/path/to/cache'));
Conditional Caching Skip caching for specific requests (e.g., POST/PUT):
$stack->push(CacheMiddleware::cacheMiddleware($cache, [
'skip_methods' => ['POST', 'PUT', 'PATCH'],
]));
Service Provider Binding
Bind the middleware and cache adapter in AppServiceProvider:
public function register()
{
$this->app->singleton(\CSA\GuzzleCacheMiddleware\Cache\Psr6Cache::class, function ($app) {
return new \Doctrine\Cache\Psr6Cache(
new \Doctrine\Common\Cache\FilesystemCache(storage_path('framework/cache/guzzle'))
);
});
}
HTTP Client Integration Attach the middleware to Laravel’s HTTP client:
$client = app(\Illuminate\Http\Client\PendingRequest::class)
->withOptions(['handler' => $stack]);
Cache Invalidation Clear cache for specific keys (e.g., after data updates):
$cache->delete('https://api.example.com/data');
Dynamic TTL Set TTL per request:
$response = $client->get('https://api.example.com/data', [
'cache_ttl' => 86400, // Override TTL for this request
]);
Cache Key Customization Use request metadata (e.g., headers) in cache keys:
'cache_key' => fn($request) => $request->getUri()->__toString() . '|' . $request->getHeaderLine('Authorization'),
Cache Stampede Protection
Use a lock (e.g., GuzzleHttp\Ring\Future\Lock) to prevent race conditions during cache misses.
Cache Key Collisions
Authorization) may collide.TTL Misconfiguration
ttl too high may serve stale data.Non-Idempotent Requests
POST/PUT requests may corrupt data.'skip_methods' => ['POST', 'PUT', 'PATCH', 'DELETE'],
Memory Leaks
FileCache) or compress responses before caching.Race Conditions
stampede_retry_after (if supported by the cache adapter).Log Cache Hits/Misses Enable debug logging for the cache adapter:
$cache = new \Doctrine\Cache\Psr6Cache(
new \Doctrine\Common\Cache\FilesystemCache(storage_path('logs/guzzle_cache')),
['logger' => \Psr\Log\LoggerInterface::class] // Inject a PSR-3 logger
);
Inspect Cache Keys Temporarily log generated cache keys:
'cache_key' => function ($request) {
$key = $request->getUri()->__toString();
\Log::debug('Cache key:', ['key' => $key]);
return $key;
},
Validate Cache Storage Manually check cache files/directory permissions:
chmod -R 775 storage/framework/cache/guzzle
Custom Cache Adapter
Implement \CSA\GuzzleCacheMiddleware\Cache\CacheInterface for non-PSR-6 caches:
class RedisCache implements CacheInterface {
public function get($key) { /* ... */ }
public function set($key, $value, $ttl) { /* ... */ }
public function delete($key) { /* ... */ }
}
Pre/Post-Processing Modify requests/responses before caching:
$stack->push(CacheMiddleware::cacheMiddleware($cache, [
'pre_process' => function ($request) {
$request = $request->withHeader('X-Cache-Enabled', 'true');
return $request;
},
'post_process' => function ($response, $request) {
return $response->withAddedHeader('X-Cache', 'HIT');
},
]));
Cache Warmup Pre-load cache for critical endpoints during deployment:
$client->get('https://api.example.com/important-data'); // Trigger cache population
Default TTL
ttl is omitted, the middleware uses a default of 300 seconds (5 minutes).Cache Key Normalization
Response Caching
cache_status_codes to override:
'cache_status_codes' => [200, 201, 301, 302],
How can I help you explore Laravel packages today?