Back to Top

Graphql Caching In Magento 2

Updated 16 December 2025

Here, we will learn about GraphQL Caching in Magento 2 and explore how to implement GraphQL Caching in Magento 2 effectively.

Caching speeds up responses and reduces server load by avoiding repeated database queries and code execution. You can cache HTTP GET requests, but the system does not cache HTTP POST requests.

The definitions for some queries include cache tags because caching uses these tags to keep track of cached content.

GraphQL allows you to make multiple queries in a single call. If you specify any uncached query, the system bypasses the cache for all queries in the call.

Let’s create a Custom module to perform GraphQL caching.

First, we will create a registration.php file inside the path below.

Start your headless eCommerce
now.
Find out More

app/code/Webkul/CustomGraphQl

<?php

\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    "Webkul_CustomGraphQl",
    __DIR__
);

Create the module.xml file in the path below.

app/code/Webkul/CustomGraphQl/etc/module.xml

<?xml version="1.0"?>

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Webkul_CustomGraphQl" setup_version="1.0.0">        
        <sequence>
                <module name="Magento_Catalog"/>
                <module name="Magento_GraphQl"/>
        </sequence>
    </module>
</config>

Now we will create a schema.graphqls file in the path below.

app/code/Webkul/CustomGraphQl/etc

This file contains the schema for your GraphQL query, like the resolver class and type.

The @doc directive defines that if we need to add some description to our query.

A resolver processes GraphQL requests by constructing queries, fetching data, and performing any necessary calculations.

Resolver transforms the fetched and calculated data into a GraphQL array format and returns the results wrapped by a callable function

@cache Directive

The @cache directive defines whether the results of certain queries can be cached.

The cacheIdentity value points to the class responsible for retrieving cache tags.

Additionally, a query without a cacheIdentity will not be cached.

Use the @cache(cacheable: false) directive to disable caching for queries defined in other modules, when a cacheIdentity class is set. This is useful when the system should not reuse cached results.

Using @cache(cacheable: true/false) has no effect if no cacheIdentity is set—the query will not be cached. To prevent caching, simply omit the @cache directive.

type Query {
    categoryProductlist (
        categoryId: Int
        pageSize: Int
        currentPage: Int
    ) : CategoryProductList @doc(description: "Get CategoryProductList") @resolver(class: "Webkul\\CustomGraphQl\\Model\\Resolver\\ProductsCollection") @cache(cacheIdentity: "Webkul\\CustomGraphQl\\Model\\Resolver\\Block\\CustomCategoryIdentity")                    
}

type CategoryProductList {
    totalCount: Int
    category_product_list: [CategoryProductListItems]
}

type CategoryProductListItems {
    entity_id: Int
    type_id: String
    sku: String
    name: String
    image: String
    status: String
    visibility: String
    price: String
}

Now we will create the resolver ProductsCollection.php in the path below because this class contains all the logic.

app/code/Webkul/CustomGraphQl/Model/Resolver

<?php

declare(strict_types=1);

namespace Webkul\CustomGraphQl\Model\Resolver;

use Magento\Framework\GraphQl\Exception\GraphQlInputException;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Framework\GraphQl\Query\ResolverInterface;

class ProductsCollection implements ResolverInterface
{
   /**
    * @var \Magento\Catalog\Model\CategoryFactory
    */
   protected $categoryFactory;
 
   /**
    * @inheritdoc
    */
   public function __construct(
        \Magento\Catalog\Model\CategoryFactory $categoryFactory,
    ) {
        $this->categoryFactory = $categoryFactory;
    }

      /**
     * @return array
     * @throws GraphQlNoSuchEntityException
     */
   public function resolve(Field $field, $context, ResolveInfo $info, ?array $value = null, ?array $args = null)
    {
        try {
            $category = $this->categoryFactory->create();
            $category->load($args['categoryId']);
            $products = $category->getProductCollection()->distinct(true);
            $products->addAttributeToSelect('*');
            $productCount = $products->getSize();
            $products->setPageSize($args['pageSize'] ?? 10);
            $products->setCurPage($args['currentPage'] ?? 1);
            $productList = [];
            foreach ($products as $product) {
                $eachProduct = [];
                $eachProduct = $product->toArray();
                $productList[] = $eachProduct;
            }
            
            $productRecord['totalCount'] = $productCount;
            $productRecord['category_product_list'] = $productList;
           
        } catch (NoSuchEntityException $e) {
            throw new GraphQlNoSuchEntityException(__($e->getMessage()), $e);
        }
        return $productRecord;
    }

        
}

Now we will create an Identity class for caching our query, named CustomCategoryIdentity.php in the path below.

app/code/Webkul/CustomGraphQl/Model/Resolver/Block

<?php
declare(strict_types=1);

namespace Webkul\CustomGraphQl\Model\Resolver\Block;

use Magento\Framework\GraphQl\Query\Resolver\IdentityInterface;

/**
 * Get identities from resolved data
 */
class CustomCategoryIdentity implements IdentityInterface
{
    private $cacheTag = 'wk_category_products_custom';

    /**
     * Get identity tags from resolved data
     *
     * @param array $resolvedData
     * @return string[]
     */
    public function getIdentities(array $resolvedData): array
    {
        $ids = [];
        $items = $resolvedData['category_product_list'] ?? [];
        foreach ($items as $item) {
            $ids[] = sprintf('%s_%s', $this->cacheTag, $item['entity_id']);
        }
        if (!empty($ids)) {
            $ids[] = $this->cacheTag;
        }
        return $ids;
    }
}

After creating all files, register the module by running bin/magento setup:upgrade, then run bin/magento setup:di:compile and clean the cache.

We will now test our GraphQL query.

Here’s an example of a request.

{
  categoryProductlist(categoryId: 2, pageSize: 20, currentPage: 1) {
    totalCount
    category_product_list {
      entity_id
      type_id
      sku
      name
      image
      status
      visibility
      price
    }
  }
}

We will get a response like below.

Selection_004

Check headers for cached params.

Selection_005

The X-Magento-Cache-Debug header shows MISS, which means the system did not cache the query.

Create a GET request with a GraphQL query because the system does not cache POST requests.

Request Type => GET

{{base_url}}/graphql?query={categoryProductlist(categoryId: 2, pageSize: 20, currentPage: 1) {totalCount category_product_list {entity_id type_id sku name image status visibility price } } }

Selection_006

Above headers show X-Magento-Cache-Debug : HIT

Now, the system caches our query.

In this way, we can achieve GraphQL caching

Create an observer that invalidates cache tags whenever the admin saves a product.

Create a file events.xml in the path below.

app/code/Webkul/CustomGraphQl/etc/adminhtml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <event name="catalog_product_save_after">
        <observer name="clear_cache_tags_custom" instance="Webkul\CustomGraphQl\Observer\Productsaveafter" />
    </event>
</config>

Now we will create an observer file named Productsaveafter.php in the path below.

app/code/Webkul/CustomGraphQl/Observer

Add the code below to the file that we have just created.

<?php

namespace Webkul\CustomGraphQl\Observer;

use Magento\Framework\Event\ObserverInterface;
use Zend_Cache;
use Magento\PageCache\Model\Cache\Type;

class Productsaveafter implements ObserverInterface
{ 
    /**
     * @var Type
     */
    private $fullPageCache;

    /**
     * @param Type $fullPageCache
     */
    public function __construct(
        Type $fullPageCache
    ) {
        $this->fullPageCache = $fullPageCache;
    }

    public function execute(\Magento\Framework\Event\Observer $observer)
    {
        $_product = $observer->getProduct();
        $productId = $_product->getId();
        $tags = ['wk_category_products_custom_'.$productId];
        if (!empty($tags)) {
            $this->fullPageCache->clean(Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG, array_unique($tags));
        }
    }   
}

In this way, we can invalidate custom cache tags for our GraphQL query.

Check for other blogs as well: https://webkul.com/blog/graphql-mutation-2/

For technical assistance, please get in touch with us via email at [email protected]

Take your Magento 2 store to the next level—browse our Magento 2 plugins page for smart enhancements.

Need something custom? Hire Magento 2 developers to build solutions tailored to your needs.

. . .

Leave a Comment

Your email address will not be published. Required fields are marked*


2 comments

  • Arun
    • Shubhanshu Sahare (Moderator)
  • Back to Top

    Message Sent!

    If you have more details or questions, you can reply to the received confirmation email.

    Back to Home