Optimising GraphQL delivery in AEM

After its introduction to AEM a few years ago, the GraphQL engine received a wide adoption and now plays a vital role for headless content delivery. Recent improvements to the engine have brought in support for filtering, sorting and paging both in the backend engine and also for the AEM headless client library.

While simple queries on small content sets like the wknd examples are easy to implement, working on large magnitude content requires well-crafted queries that use the new features with some understanding of the underlying query engine to ensure queries run smoothly even under heavy load.

This presentation provides more insight into AEM's GraphQL query engine, provides a thorough comparison of different query types and talks about hybrid filtering techniques you could employ in your apps. In addition, we will touch on other aspects of GraphQL delivery optimisations, like persisted queries and how to configure the AEM dispatcher for efficient GraphQL delivery.

After this session attendees will be able to use the AEM headless client to produce performant queries, avoid common pitfalls and learn new optimisation tricks to guarantee the best content delivery performance.


Can't you also make use of (oak / lucene) indexes to speed up GraphQL queries?

Evgeny Tugarev

this is already by design, all indexes are lucene

Tomasz S

Did you consider shifting the paradigm completely and stop querying AEM in realtime, and instead push the aem cf data into a dedicated engine which you could query without impacting aem & jcr?

Georg Henzler

Potentially with an approach like that you could squeeze out "a little bit more of extra performance" (depending on how you implement it) but it's quite a bit more complicated. For now in the ootb product it's KISS and the JCR is the "one place of truth" that is queried directly.


Why there is no smart cache invalidation built in? Publishing of fragments does not invalidate the associated caches (persisted queries). Having to build an own solution for this is demotivating

Evgeny Tugarev

Good question 👍 For invalidation of persisted queries we currently leverage TTL's, this is valid as for Dispatcher as for caching at the CDN level. You can find more information in about it https://experienceleague.adobe.com/docs/experience-manager-cloud-service/content/headless/graphql-api/persisted-queries.html?lang=en#caching-persisted-queries I would like to understand your use case more to see if there is something we can do about it, please come to playground session.

Konrad Windszus

Which code piece writes the additional indexedData node in case one directly creates CFs with Sling/JCR API?

Evgeny Tugarev

Content Fragments API (Java) does that, as soon as you have a property /content/dam/cfGlobalVersion = (Long)1 set. Content fragment's update procedure sets this flag once you have all your content fragments updated. You will find the links to the update procedure (for both AEMaaCS and 6.5) and also to the API's in the References section of the presentation. and You should neither use Sling nor JCR API to manipulate CFs directly - only CF APIs (HTTP or Java)


Could you please elaborate on the "use high level API" part

Georg Henzler

Use either the CF Http API or the AEM Java API (https://developer.adobe.com/experience-manager/reference-materials/cloud-service/javadoc/com/adobe/cq/dam/cfm/ContentFragment.html) - this will ensure the content structure in JCR will be as the other parts of the product will expect it to be

"Use persisted queries" - why use GraphQL if you need to fix so many issues to get it to work as nicely as REST? This industry is broken.

Georg Henzler

I like REST too :) However GraphQL makes totally sense for specific use cases (tailored queries across many entities with a response trimmed to the exact payload needed)