Installation Add the package via Composer:
composer require api-platform/http-cache
Basic Configuration
Register the HttpCache middleware in app/Http/Kernel.php:
protected $middlewareGroups = [
'api' => [
// ...
\ApiPlatform\HttpCache\Middleware\CacheMiddleware::class,
],
];
First Use Case
Enable caching for a single API resource by adding the Cache attribute to your entity:
use ApiPlatform\Core\Annotation\Cache;
#[Cache(maxAge: 3600)]
class Book {}
Now, API responses for Book will be cached for 1 hour.
Entity-Level Caching
Use the #[Cache] attribute to define cache behavior per resource:
#[Cache(maxAge: 60, public: true)]
class Product {}
maxAge: Cache lifetime in seconds (default: 0 = no caching).public: Allow caching by proxies/CDNs (default: false).Collection vs. Item Caching
Differentiate between collection (/api/books) and item (/api/books/1) caching:
#[Cache(maxAge: 300, public: true)]
#[ApiResource(collectionOperations: ['get'], itemOperations: ['get'])]
class Book {}
Dynamic Cache Keys Override the cache key logic for complex scenarios:
use ApiPlatform\Core\Annotation\Cache;
use ApiPlatform\HttpCache\CacheKeyGeneratorInterface;
#[Cache(keyGenerator: MyCustomCacheKeyGenerator::class)]
class User {}
Integration with API Platform
Combine with #[ApiResource] for seamless caching of serialized data:
#[ApiResource]
#[Cache(maxAge: 1800)]
class Article {}
Cache Invalidation Manually invalidate cache for a resource after updates:
use ApiPlatform\HttpCache\CacheInvalidatorInterface;
public function __construct(private CacheInvalidatorInterface $cacheInvalidator) {}
public function update(Article $article, UpdateArticleDto $data): Article {
$article = $this->updateEntity($article, $data);
$this->cacheInvalidator->invalidate($article);
return $article;
}
Symfony Cache Component
The package relies on Symfony’s Cache component. Configure it in config/packages/framework.yaml:
framework:
cache:
app: cache.adapter.redis
Varnish/Nginx Integration
For public: true caches, ensure your web server is configured to honor Cache-Control headers:
location /api {
proxy_cache my_cache;
proxy_cache_valid 200 302 1h;
proxy_cache_valid 404 1m;
}
Testing
Mock the CacheMiddleware in PHPUnit:
$this->app->instance(CacheMiddleware::class, $mockMiddleware);
Cache Headers Overrides
Custom serializers or filters may override Cache-Control headers. Ensure they respect the #[Cache] attribute:
public function normalize($object, string $format, array $context = []): array {
$context['cache_headers'] = ['Cache-Control' => 'public, max-age=3600'];
return [...];
}
ETag/Last-Modified Conflicts
If your API uses #[ApiResource] with #[Cache], ensure ETag or Last-Modified headers are not conflicting with Cache-Control. Disable one if needed:
#[Cache(maxAge: 3600, vary: ['Accept'])]
class Post {}
Private Cache with Auth
Avoid caching authenticated responses. Use public: false (default) and ensure Authorization headers are excluded from cache keys:
#[Cache(maxAge: 0, public: false)]
class UserProfile {}
Cache Key Collisions Dynamic cache keys (e.g., based on query params) may cause collisions. Use unique suffixes:
#[Cache(keyGenerator: class implements CacheKeyGeneratorInterface {
public function generate(string $uri, array $context): string {
return md5($uri . serialize($context['query']));
}
})]
class SearchResult {}
Middleware Order
Place CacheMiddleware after ApiPlatform\Metadata\Middleware\CheckVersionMiddleware and before ApiPlatform\Metadata\Middleware\UnderscoreNormalizerMiddleware in Kernel.php.
Check Headers
Inspect responses for Cache-Control, X-Cache, or X-Cache-Hits headers:
curl -I http://your-api/books
Enable Debug Mode
Temporarily disable caching in config/packages/api_platform.yaml:
api_platform:
http_cache:
enabled: false
Log Cache Events Add a subscriber to log cache hits/misses:
use ApiPlatform\HttpCache\EventListener\CacheListener;
public function onKernelResponse(ResponseEvent $event): void {
$response = $event->getResponse();
if ($response->headers->has('X-Cache')) {
$this->logger->info('Cache: ' . $response->headers->get('X-Cache'));
}
}
Custom Cache Store
Replace the default CacheItemPoolInterface:
$this->app->bind(CacheItemPoolInterface::class, function () {
return new RedisCachePool(new RedisClient());
});
Event Subscribers
Listen to cache events (e.g., CacheEvent::INVALIDATE):
use ApiPlatform\HttpCache\Event\CacheEvent;
public static function getSubscribedEvents(): array {
return [
CacheEvent::INVALIDATE => 'onCacheInvalidate',
];
}
Conditional Caching Dynamically enable/disable caching based on runtime logic:
use ApiPlatform\HttpCache\Middleware\CacheMiddleware;
$middleware = new CacheMiddleware($cachePool, $cacheKeyGenerator);
if ($this->shouldCache()) {
$response = $middleware->handle($request, $next);
} else {
$response = $next($request);
}
Cache Warmup Pre-warm cache for critical endpoints:
use Symfony\Component\HttpClient\HttpClient;
$client = HttpClient::create();
$client->request('GET', '/api/homepage');
How can I help you explore Laravel packages today?