45 min read
Published:
Hello everyone! I'm back in 2025 after a three-month hiatus—I've been busy moving to a new home, which hasn't left much free time. I've also started a new position at Velir, where I'm working extensively with XM Cloud implementations. While I had some experience before, I'm now diving much deeper into the technical details, and I'll be sharing these insights with you. Today, I'll explain how I solved a particular XM Cloud challenge.
Recently, I created a complex component in Sitecore XM Cloud using Integrated GraphQL (Integrated GraphQL in JSS apps | Sitecore Documentation). While building this component, I encountered a challenge—the component required a complex GraphQL query with multiple nested levels, which triggered the following error:
1{2 "errors": [3 {4 "message": "Query is too nested to execute. Depth is 17 levels, maximum allowed on this endpoint is 15.",5 "extensions": {6 "code": "INVALID_OPERATION"7 }8 }9 ]10}
After encountering this issue, I needed to determine the best solution. While searching online, I found that most articles recommended patching the XM Cloud configuration—which is strongly discouraged in Sitecore XM Cloud. This reminded me of my early days learning Sitecore: just because someone writes an article about a solution doesn't mean it's the right approach. Though patching a configuration isn't quite as problematic as creating custom .NET extensions, it was evident this wasn't the optimal path forward. Here's an example of the patch configuration method that I decided against:
1<?xml version="1.0" encoding="utf-8"?>2<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"3 xmlns:set="http://www.sitecore.net/xmlconfig/set/"4 xmlns:role="http://www.sitecore.net/xmlconfig/role/"5 xmlns:env="http://www.sitecore.net/xmlconfig/env/">6 <sitecore>7 <api>8 <GraphQL>9 <defaults>10 <security>11 <publicService type="Sitecore.Services.GraphQL.Hosting.Security.GraphQLSecurity, Sitecore.Services.GraphQL">12 <complexityConfiguration type="GraphQL.Validation.Complexity.ComplexityConfiguration, GraphQL">13 <maxDepth>15</maxDepth>14 <maxComplexity>2000</maxComplexity>15 <fieldImpact>2</fieldImpact>16 </complexityConfiguration>17 </publicService>18 </security>19 </defaults>20 </GraphQL>21 </api>22 </sitecore>23</configuration>
This experience inspired me to write this blog post once I found a solution. It's worth noting that I tried different GraphQL approaches, switching from a datasource query to a search query. However, the query depth limit remained the same regardless of the GraphQL type used.
As the title suggests, I needed to split up the query to retrieve all necessary data without triggering the error mentioned above. Since these pages are statically generated, making multiple requests is a reasonable approach—it's far less problematic than it would be if we were making these requests on every page load.
While researching this topic, I identified key requirements for the correct approach. The component needed to render on initial page load rather than loading dynamically afterward. While there are cases where you might want an initial interface followed by asynchronous data fetching from Edge GraphQL, I wanted everything to load seamlessly at once. Though both options I'll discuss would satisfy this requirement, I also had to consider how each approach would affect the content editing experience within pages. Before diving into these two approaches, I should mention the Sitecore XM Cloud Accelerate documentation, which thoroughly explains the benefits and drawbacks of the different data fetching methods: Getting Component Specific Data | Accelerate Cookbook for XM Cloud | Sitecore Developer Portal.
The first approach is separating the inner components and their data into Placeholders. The issue, as I alluded to above, is that this complicates Page Builder. A component that could be handled as a single unit would now require content authors to drop one component and then immediately drop a second component. This isn't ideal due to that extra complexity. There has to be a better way.
However, this placeholder method can make sense in certain cases, particularly when data is difficult to manage elsewhere and you want to give content authors flexibility in building components with multiple nested elements. For cases where the data structure remains constant, you may want to consider using the Linked List approach, which I'll discuss in a future post.
Let's explore the better approach: component-level data fetching. This method allows your component to fetch any additional data needed for your website. Whether it's third-party data or, in this case, breaking down a complex GraphQL query into manageable pieces. I chose to remove all Integrated GraphQL (though I could have simply simplified it to include only what I needed) since working without it seemed more straightforward. I implemented GetStaticProps
at the component level. For those familiar with traditional Next.js development, this might seem unusual—typically, you'd pass complex objects at the page level and drill props down to their respective components. However, JSS enables these additional requests at the component level, which both simplifies data access and eliminates the need for extensive prop drilling from the page component.
Important note: Since getStaticProps
and getServerSideProps
are bundled on the client side, be cautious not to include any sensitive information in these functions.
Let's take a closer look at the code for our component. The original complex component wired up with XM Cloud handles only its datasource fields, while I use those same fields to construct additionalFields
, which I hydrate from the getStaticProps
function:
1export type HeaderFields = {2 additionalFields: {3 item: {4 megaMenuLinks: {5 targetItems: {6 ...7 }8 }9 }10 };11 fields: {12 logo: ImageField;13 fieldA: TextField;14 fieldB: TextField;15 fieldC: TextField;16 ...17 }18}1920export HeaderProps = ComponentProps & PlaceholderProps & HeaderFields;2122const Default = (props: HeaderProps): JSX.Element => {23 const { fields, additionalFields} = props;2425 ... (Rest of the Code to Render Fields)26}2728export const getStaticProps: GetStaticComponentProps = async (rendering, layoutData) => {29 const graphQlClient = graphqlClientFactory();3031 const response = await graphQlClient.request<HeaderAdditionalFields>(headerQuery, { datasource: rendering.dataSource, language: layoutData.sitecore.context.language, });3233 return {34 externalFields: response || {},35 };36};37
To complete the picture, I should mention that headerQuery
is simply a datasource GraphQL query stored in a separate TypeScript file.
When you find recommendations to customize the patch config, avoid that approach. Instead, re-engineer your component and break up your request into smaller, manageable pieces. Patching the configuration is a slippery slope—while we might only need a few more nested levels now, future queries could require even more depth, tempting us to keep increasing the limit beyond what's appropriate. This approach also isn't future-proof: what happens if Sitecore removes the ability to customize this configuration? As XM Cloud developers, we must build solutions that will stand the test of time.