Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

Caching GraphQL Responses and the Full Page Cache

Caching GraphQL Responses and the Full Page Cache

~8 Min. Lesezeit Zuletzt aktualisiert am August 9, 2026

Batch resolvers (chapter 20) reduce the number of database queries per request. Caching operates a level higher: not recomputing an entire GraphQL response at all when an identical request has already been served. This chapter shows how Magento's full page cache interacts with GraphQL - and where the limits of that mechanism lie, especially for personalized fields like is_favorite.

GraphQL responses land in the same FPC as any other page

Magento fundamentally treats /graphql responses like any other HTTP response in the full page cache: an X-Magento-Cache-Id header is computed from the query string, variables, store, and website context, and an identical follow-up request is served straight from the cache, without a single resolver running again.

Authenticated requests aren't cached publicly

The key protection mechanism for personalized fields like is_favorite: as soon as a request carries an Authorization header with a customer token (chapter 17), Magento marks the response as not publicly cacheable. A logged-in customer therefore never accidentally receives the FPC-stored "is_favorite: false" response from another (guest) request - requests with a customer token always run fresh through every resolver.

Achtung: The flip side: once a query includes is_favorite or another personalized field, it should only be sent with a valid token - a guest request for is_favorite correctly returns false (chapter 17), but that exact "always false" response then lands publicly cacheable in the FPC, which is unproblematic for purely anonymous requests but can easily cause confusion if misused.

Cache tags for the custom events query

For anonymous, publicly cached requests, a second problem remains: if an event gets changed in the admin, the corresponding FPC entry needs invalidating - otherwise the cache serves stale data. Resolvers implementing \Magento\Framework\GraphQl\Query\Resolver\IdentityInterface can attach their own cache tags to the response for this purpose:

app/code/Mironsoft/Event/Model/Resolver/Events.php (extended interface)
<?php

declare(strict_types=1);

namespace Mironsoft\Event\Model\Resolver;

use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Query\Resolver\IdentityInterface;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Mironsoft\Event\Model\Resolver\DataProvider\Events as EventsDataProvider;

/**
 * Resolves the events query field and tags its FPC entry per event.
 */
class Events implements ResolverInterface, IdentityInterface
{
    private const CACHE_TAG = 'mironsoft_event';

    /**
     * @param EventsDataProvider $eventsDataProvider Loads and shapes event list data
     */
    public function __construct(
        private readonly EventsDataProvider $eventsDataProvider,
    ) {
    }

    // resolve() unchanged from chapters 13/14

    /**
     * Returns the cache tags this resolved data should be invalidated by.
     *
     * @param array<string, mixed> $resolvedData The array returned by resolve()
     * @return string[]
     */
    public function getIdentities(array $resolvedData): array
    {
        $tags = [self::CACHE_TAG];

        foreach ($resolvedData['items'] ?? [] as $item) {
            if (isset($item['event_id'])) {
                $tags[] = self::CACHE_TAG . '_' . $item['event_id'];
            }
        }

        return $tags;
    }
}

When an event is saved in the admin (e.g. through a future save controller following the pattern from the Admin Grids & Forms series), the Event model would consistently need to implement \Magento\Framework\DataObject\IdentityInterface and return the same tag prefix - only then does Magento's automatic cache invalidation on save kick in throughout.

What caching doesn't replace

Caching lowers response times for repeated, identical requests - it doesn't solve the N+1 problem from chapter 20 for the first request that fills the cache, and it doesn't help with mutations, which by nature can never be served from the cache. Both optimization layers complement each other, but don't replace one another.

Tipp: The fastest way to check a response's cache status is the X-Magento-Cache-Debug header (HIT/MISS), visible once the full page cache is configured in debug mode - handy for verifying that authenticated requests are indeed never marked as a HIT.

Chapter 22 switches from read to write performance topics: how to upload files such as images over GraphQL at all, given the endpoint has no notion of classic multipart uploads.