Sitecore Headless SXA GraphQL Queries on Content Delivery Servers

I ran into this issue recently where no matter what I did, I could not get my Sitecore 10.3 headless SXA content delivery server to return a valid value for the rendered property in a GraphQL query, whether it was the layout service or the item service.

So, for example, if you had a query like this:

{
  layout(site: "mysite", routePath: "/", language: "en") {
    item {
      name
      rendered
    }
  }
}

on the content management server, it would return correctly what’s expected:

{
    "data": {
        "layout": {
            "item": {
                "name": "Home",
                "rendered": {
                  [rendering outputs]
                  ...
                  ...
                  ...
                }
            }
        }
    }
}

but on the content delivery server, it would always return this for the rendered property, no matter the path:

{
    "data": {
        "layout": {
            "item": {
                "name": "Home",
                "rendered": {}
            }
        }
    }
}

This happened on a fresh installation of XP 10.3, so I knew this wasn’t an issue with custom code or config - because there wasn’t any. Also, no errors in the logs. It’s like it just…didn’t want to work.

To investigate this, I decompiled the getLayoutData pipeline processor:

<processor type="Sitecore.LayoutService.GraphQL.Pipelines.GetLayoutData.RenderItem, Sitecore.LayoutService.GraphQL" resolve="true"/>

and stepped through it. Sure enough, the debugger lit up like a Christmas tree, looking for a master database. There’s no master database reference on the content delivery role! Now to find where it’s referencing it.

A little more investigation and I found my culprit in these lines in Sitecore.XA.JSS.Foundation.SiteMetadata.config:

  <sitecore>
    <layoutService>
      <configurations>
        <config name="sxa-jss">
          <requestContext type="Sitecore.LayoutService.Configuration.DefaultRequestContext, Sitecore.LayoutService">
            <databaseName>master</databaseName>
          </requestContext>
  ...

That’s right, the default request context for the sxa-jss config for GraphQL queries explicitly specifies the master database. As there’s no role:require specified, this applied to both content management and content delivery instances. The ironic thing is that the code for DefaultRequestContext actually defaults to the context database (master when in instances like preview mode, web in published mode) unless you explicitly specify one.

If you’re wondering what this means, this means Sitecore headless SXA does not work out of the box with content delivery servers. Let that sink in there for a second.

So, the fix turns out to be simple - patch out the <databaseName>master</databaseName> line in the config and let it use the context database.

The full patch file is below:

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:set="http://www.sitecore.net/xmlconfig/set/">
  <sitecore>
    <layoutService>
      <configurations>
        <config name="sxa-jss">
          <!-- This removes the hardcoded database to be used as the request context and allows it to use the context database -->
          <requestContext type="Sitecore.LayoutService.Configuration.DefaultRequestContext, Sitecore.LayoutService">
            <databaseName>
              <patch:delete />
            </databaseName>
          </requestContext>
        </config>
      </configurations>
    </layoutService>
  </sitecore>
</configuration>

While discussing with Sitecore rockstar Corey Smith, he mentioned that he actually ran into this issue before I did (of course he did) and logged a ticket. Instead of patching it in a hotfix or something, Sitecore put it in a KB: https://support.sitecore.com/kb?id=kb_article_view&sysparm_article=KB1003354

They recommend patching it with web for CD servers, but if you have other databases other than a web database, it’ll break…again. Better to let it use the context database per above.

This appears to have been fixed in Sitecore 10.4 by only specifying master on the ContentManagement and XMCloud roles:

  <sitecore>
    <layoutService>
      <configurations>
        <config name="sxa-jss">
          <requestContext type="Sitecore.LayoutService.Configuration.DefaultRequestContext, Sitecore.LayoutService">
            <databaseName role:require="ContentManagement or XMCloud">master</databaseName>
          </requestContext>
  ...

Hope this helps anyone else who has ran into this issue!