jacyimp / api-platform-operation-cache
Operation-level response caching for API Platform
Package info
github.com/jacyimp/api-platform-operation-cache
Type:symfony-bundle
pkg:composer/jacyimp/api-platform-operation-cache
Requires
- php: ^8.2
- api-platform/metadata: ^3.4 || ^4.0
- psr/cache: ^3.0
- psr/container: ^2.0
- psr/event-dispatcher: ^1.0
- symfony/config: ^6.4 || ^7.0 || ^8.0
- symfony/dependency-injection: ^6.4 || ^7.0 || ^8.0
- symfony/http-foundation: ^6.4 || ^7.0 || ^8.0
- symfony/http-kernel: ^6.4 || ^7.0 || ^8.0
Requires (Dev)
- api-platform/core: ^3.4 || ^4.0
- illuminate/cache: ^11.0 || ^12.0 || ^13.0
- illuminate/contracts: ^11.0 || ^12.0 || ^13.0
- illuminate/http: ^11.0 || ^12.0 || ^13.0
- illuminate/routing: ^11.0 || ^12.0 || ^13.0
- illuminate/support: ^11.0 || ^12.0 || ^13.0
- orchestra/testbench: ^9.0 || ^10.0 || ^11.0
- phpstan/phpstan: ^2.2.13
- phpstan/phpstan-deprecation-rules: ^2.0
- phpstan/phpstan-phpunit: ^2.0
- phpstan/phpstan-strict-rules: ^2.0
- phpunit/phpunit: ^11.5
- slevomat/coding-standard: ^8.31
- squizlabs/php_codesniffer: ^4.0.4
- symfony/browser-kit: ^6.4 || ^7.0 || ^8.0
- symfony/cache: ^6.4 || ^7.0 || ^8.0
- symfony/framework-bundle: ^6.4 || ^7.0 || ^8.0
- symfony/security-core: ^6.4 || ^7.0 || ^8.0
Suggests
- api-platform/laravel: Required when using the Laravel integration.
- symfony/security-core: Required when using varyByAuth with Symfony.
Provides
None
Conflicts
None
Replaces
None
README
Cache API Platform Get and GetCollection responses in Symfony or Laravel. Cache hits skip provider/controller processing. Caching is opt-in per operation.
Usage
Add OperationCache to the operations you want to cache:
use ApiPlatform\Metadata\ApiResource; use ApiPlatform\Metadata\Get; use ApiPlatform\Metadata\GetCollection; use JacyImp\ApiPlatformOperationCache\Metadata\OperationCache; #[ApiResource(operations: [ new Get(extraProperties: [ new OperationCache(ttl: 300), ]), new GetCollection(extraProperties: [ new OperationCache(ttl: 60), ]), // Keep your existing write and custom operations here. ])] final class Product { // Your resource fields... }
ttl is required and must be a positive number of seconds. Matching requests reuse the stored response until expiry. Query parameters give pages and filters separate entries. For user-specific content, vary by authenticated identity.
Install
composer require jacyimp/api-platform-operation-cache
Requires PHP ^8.2, API Platform Metadata ^3.4 || ^4.0, and Symfony components ^6.4 || ^7.0 || ^8.0. Laravel apps also need api-platform/laravel.
Symfony
Register the bundle in config/bundles.php:
use JacyImp\ApiPlatformOperationCache\Symfony\ApiPlatformOperationCacheBundle; return [ // Existing bundles... ApiPlatformOperationCacheBundle::class => ['all' => true], ];
It uses cache.app by default.
Choose a cache pool or configure services →
Laravel
Package discovery adds the cache middleware to API Platform's default middleware configuration. It uses Laravel's default cache store.
Invalidate after writes
Assign groups to cached reads and invalidate them on successful writes:
use ApiPlatform\Metadata\ApiResource; use ApiPlatform\Metadata\Delete; use ApiPlatform\Metadata\Get; use ApiPlatform\Metadata\GetCollection; use ApiPlatform\Metadata\Patch; use ApiPlatform\Metadata\Post; use JacyImp\ApiPlatformOperationCache\Metadata\OperationCache; use JacyImp\ApiPlatformOperationCache\Metadata\OperationCacheInvalidation; #[ApiResource(operations: [ new Get(extraProperties: [ new OperationCache(ttl: 300, groups: ['product:{id}']), ]), new GetCollection(extraProperties: [ new OperationCache(ttl: 60, groups: ['products']), ]), new Post(extraProperties: [ new OperationCacheInvalidation(group: 'products'), ]), new Patch(extraProperties: [ new OperationCacheInvalidation(group: 'product:{id}'), new OperationCacheInvalidation(group: 'products'), ]), new Delete(extraProperties: [ new OperationCacheInvalidation(group: 'product:{id}'), new OperationCacheInvalidation(group: 'products'), ]), ])] final class Product { // Your existing resource fields and persistence mapping... }
In this example, Post invalidates cached product collections; Patch and Delete also invalidate the product's detail response. Failed operations leave caches intact. The next matching read rebuilds the response.
{id} uses the operation's URI variable name. Apply the same rules to Put and custom writes as needed. Writes outside these operations require explicit invalidation.
Advanced groups, conditional invalidation, and invalidating from application code
Vary by headers
The cache key already includes the operation, host, method, path, query parameters, and request format. Add headers that change your response:
new OperationCache( ttl: 300, varyByHeaders: ['Accept-Language', 'X-Currency'], )
Default vary headers are empty. Operations inherit configured defaults unless includeDefaultVary: false is set. Use varyByAuth for authenticated identity.
Cache user-specific responses
varyByAuth defaults to false. Enable it for responses that depend on the authenticated user:
new OperationCache( ttl: 300, varyByAuth: true, )
Each authenticated identity gets separate entries; anonymous requests share an anonymous identity. Symfony uses getUserIdentifier() (requires Symfony Security); Laravel uses getAuthIdentifier().
Vary by tenant, account, or custom context →
Customize caching
OpenAPI / Swagger documentation
Cached operations automatically include a Caching section in their OpenAPI description (shown in Swagger UI) in Symfony and Laravel. It documents the TTL in seconds, cache key variation (including inherited default headers and authenticated identity), conditional caching, and response exclusions. Existing descriptions and other OpenAPI settings are preserved. Operations without OperationCache and operations hidden with openapi: false are unchanged.
| Use case | Guide |
|---|---|
| Skip caching for previews or other requests | Conditional caching |
| Exclude request IDs or trace headers | Response headers |
| Modify the stored response or add headers on cache hits | Response mutators |
| Inspect cache keys, exclusions, and option defaults | Cache behavior and options |
| Observe hits, misses, stores, and invalidations | PSR-14 lifecycle events |
Only successful GET/HEAD responses are stored. Streams, binary files, and responses with no-store, Set-Cookie, or Vary: * are skipped.
Development
composer check
Runs Composer validation, coding standards, static analysis, and tests.
License
MIT.