Response Caching
GraphQL Cache Configuration
<!-- etc/di.xml -->
<config>
<type name="Magento\Framework\GraphQl\Controller\GraphQl">
<plugin name="graphql_cache" type="Vendor\Module\Plugin\GraphQlCachePlugin"/>
</type>
</config>
Cache Plugin
<?php
namespace Vendor\Module\Plugin;
class GraphQlCachePlugin
{
public function __construct(
private \Magento\Framework\Cache\FrontendInterface $cache,
) {
}
public function beforeExecute(
\Magento\Framework\GraphQl\Controller\GraphQl $subject
): void {
$cacheKey = $this->getCacheKey($subject->getRequest());
$cachedResponse = $this->cache->load($cacheKey);
if ($cachedResponse) {
$subject->getResponse()->setContent($cachedResponse);
return false; // Skip execution
}
}
public function afterExecute(
\Magento\Framework\GraphQl\Controller\GraphQl $subject,
$result
) {
$cacheKey = $this->getCacheKey($subject->getRequest());
$this->cache->save(
$subject->getResponse()->getContent(),
$cacheKey,
['graphql_cache']
);
return $result;
}
}
Cache Key Generation
private function getCacheKey($request): string
{
$body = $request->getContent();
$hash = md5($body);
return 'graphql_' . $hash;
}
CDN Caching
CDN Headers for GraphQL
public function execute()
{
$response = $this->graphQl->execute();
// Add cache headers
$response->setHeader('Cache-Control', 'public, max-age=3600');
$response->setHeader('X-Cache-Tag', 'graphql_products');
return $response;
}
Nginx Configuration
# Cache GraphQL queries
location /graphql {
# Only cache GET requests
if ($request_method != POST) {
return 405;
}
# Cache for 1 hour
add_header Cache-Control "public, max-age=3600";
add_header X-Cache-Status $upstream_cache_status;
# Vary by authorization
add_header Vary "Authorization";
proxy_pass http://backend;
}
Varnish Configuration
# Cache GraphQL responses
sub vcl_backend_response {
if (req.url ~ "^/graphql") {
# Cache for 1 hour
set beresp.ttl = 1h;
# Don't cache private content
if (beresp.http.Authorization) {
set beresp.uncacheable = true;
set beresp.ttl = 0s;
}
}
}
CDN Strategies
| Strategy | Use Case |
|---|---|
| Public cache | Product catalog, categories |
| Private cache | Customer data, cart |
| No cache | Real-time inventory |
| Short TTL | Pricing, promotions |
Cache Tags
Cache Tag Implementation
public function resolve(
Field $field,
$context,
ResolveInfo $info,
array $value = null,
array $args = null
) {
$product = $this->productRepository->get($args['sku']);
// Return with cache tags
return [
'data' => [
'id' => $product->getId(),
'name' => $product->getName()
],
'cache_tags' => [
'product_' . $product->getId(),
'category_' . $product->getCategoryId()
]
];
}
Cache Tag Invalidation
public function saveProduct($product)
{
$this->productRepository->save($product);
// Invalidate cache tags
$tags = [
'product_' . $product->getId(),
'product_list',
'category_' . $product->getCategoryId()
];
$this->cache->clean($tags);
}
Tag-Based Caching
private function getCacheTags(array $args): array
{
$tags = ['graphql'];
if (isset($args['filter']['category_id'])) {
$tags[] = 'category_' . $args['filter']['category_id'];
}
if (isset($args['filter']['sku'])) {
$tags[] = 'product_sku_' . $args['filter']['sku'];
}
return $tags;
}
Private Content
Private Content Handling
public function resolve(
Field $field,
$context,
ResolveInfo $info,
array $value = null,
array $args = null
) {
// Check if request is for private content
$isPrivate = $this->isPrivateContent($info);
if ($isPrivate) {
// Don't cache private content
$context->addCacheFlag('no-cache');
// Or use short TTL
$context->setCacheTtl(60); // 1 minute
}
return $this->getData($args);
}
Customer-Specific Caching
private function getCacheKey(array $args, $context): string
{
$key = 'graphql_' . md5(json_encode($args));
// Add customer ID for private content
if ($context->getExtensionAttributes()->getCustomerGraphQlContext()) {
$customerId = $context->getExtensionAttributes()
->getCustomerGraphQlContext()
->getCustomerId();
$key .= '_customer_' . $customerId;
}
return $key;
}
Cache Control Headers
public function execute()
{
$response = $this->graphQl->execute();
// Private content - no cache
if ($this->isPrivateRequest()) {
$response->setHeader('Cache-Control', 'no-store, no-cache, must-revalidate');
$response->setHeader('Pragma', 'no-cache');
}
// Public content - cache with tags
else {
$response->setHeader('Cache-Control', 'public, max-age=3600');
$response->setHeader('X-Cache-Tags', implode(',', $this->getCacheTags()));
}
return $response;
}
Quiz
1. How do you cache GraphQL responses?
2. What are cache tags used for?
3. How do you handle private content?
Flashcards
Question
How do you cache GraphQL responses?
Click to reveal answer
Answer
Implement cache plugin with cache key generation
Question
What are cache tags?
Click to reveal answer
Answer
Tags that allow selective cache invalidation
Question
How do you handle private content?
Click to reveal answer
Answer
Don't cache or use short TTL
Question
What CDN header controls caching?
Click to reveal answer
Answer
Cache-Control: public, max-age=3600
Question
How do you invalidate cache?
Click to reveal answer
Answer
Use cache tags and clean specific tags
Revision Notes
Key Takeaways
- 1. Implement cache plugins for GraphQL responses
- 2. Use cache tags for selective invalidation
- 3. Handle private content with no-cache or short TTL
- 4. Configure CDN headers for caching
- 5. Vary cache by authorization header
Interview Tips
- • Explain GraphQL caching strategies
- • Know how to implement cache plugins
- • Discuss cache invalidation approaches
- • Be ready to handle private content
Cheat Sheet
Cache:
Cache-Control: public, max-age=3600
X-Cache-Tags: product_1,category_5
Private:
Cache-Control: no-store, no-cache
Invalidation:
$cache->clean(['product_1', 'product_list'])
CDN:
Vary: Authorization
Cache by query hash