Swapping your XM Cloud Editing Host to Content SDK

In the previous post I talked about how to wire up Sitecore Pages to use your local Content SDK editing host. This post will describe how to switch it in your deployed XM Cloud environment.

If you created your XM Cloud project using the auto-generated repository, you got a clone of the XM Cloud starter kit. That project, currently, includes 2 head apps: Next.js and Angular. By default, when you deploy your project, the Next.js app is set up as the editing host. You’ve now brought in a Content SDK app and want to switch the editing host to that one. So, how do you do it?

Turns out, it’s actually pretty simple. In your project root, look for xmcloud.build.json. Open that file, and add this to the renderingHosts section:

"contentsdk": {
  "path": "./headapps/nextjs-content-sdk",
  "nodeVersion": "22.11.0",
  "jssDeploymentSecret": "<your deployment secret>",
  "enabled": true,
  "type": "sxa",
  "buildCommand": "build",
  "runCommand": "next:start"
}

The path should be the path to the folder containing the Content SDK app. You’ll also want to set enabled to true, and set it to false for the other rendering hosts (or just remove those other hosts from this file altogether).

Shout out to Dan Solovay for the assist on this one!

Connecting your Sitecore Content SDK app to local XM Cloud in Docker

The Sitecore XM Cloud Content SDK was recently released, and it offers a trimmed down, focused kit to build websites with XM Cloud. However, this new SDK hasn’t been put into the default starter kit codebase that gets created when you start a new project. This post will show you how to get your local development environment up and running with the Content SDK, and how to connect it to the Pages editor.

Create your project in XM Cloud Deploy

    This step is straightforward. Use XM Cloud deploy to create your project, environment, and codebase. This will create you a repository from the xmcloud-foundation-head repository that Sitecore provides. You can also use the code-first approach if you so choose, but for the sake of simplicity this post assumes you let Sitecore create your project.

    When the initial deployment is created, go ahead and create your site in the Sitecore Cloud portal. Choose the blank site starter template.

    Once that process completes, go to the Desktop (you can find the Desktop from the Settings screen in the portal) and package up your site. You’ll want this to install to your local environments soon. You should also consider adding a Page Content component to the page to see something other than a blank screen when you load the site.

    These are the paths you’ll need to package:

    • /sitecore/templates/Project/<Your Site Collection>
    • /sitecore/templates/Branches/Project/<Your Site Collection>
    • /sitecore/system/Settings/Project/<Your Site Collection>
    • /sitecore/media library/Project/<Your Site Collection>
    • /sitecore/layout/Placeholder Settings/Project/<Your Site Collection>
    • /sitecore/layout/Renderings/Project/<Your Site Collection>
    • /sitecore/layout/Placeholder Settings/Project/<Your Site Collection>
    • /sitecore/content/<Your Site Collection>

    Set up your local environment

    Once your project is created, clone your new repo and run through the steps to set up your local environment with Docker. Follow the instructions here: https://doc.sitecore.com/xmc/en/developers/xm-cloud/set-up-your-full-stack-xm-cloud-local-development-environment.html#prepare-the-environment

    Once your environment is up and running, install the package you just created to your local CM, located at: https://xmcloudcm.localhost/sitecore/

    You’ll need to get some environment variables from your local rendering host. Some of these are created when you run init.ps1 to scaffold your containers environment. You can get these from the Docker container, or from the .env file in the /local-containers folder in your project root.

    Copy the JSS_EDITING_SECRET and SITECORE_API_KEY values.

    Create the Content SDK project

    Next you’ll want to create the Content SDK project in your local environment. The documentation is located here: https://doc.sitecore.com/xmc/en/developers/content-sdk/create-a-content-sdk-app-locally.html

    1. Open a terminal in the /headapps folder of your project root
    2. Run the following command: npx @sitecore-content-sdk/create-sitecore-jss nextjs

    This will create a new folder under /headapps called next.js. The new Content SDK project is here.

    Connect the Content SDK project to your local CM

    To connect the new project to XM Cloud, the steps are described here: https://doc.sitecore.com/xmc/en/developers/content-sdk/connect-your-content-sdk-app-to-xm-cloud.html

    To connect to a local CM running in Docker, the steps are similar. Create a .env.local file in the root of your Content SDK project. Add the follwing environment variables:

    NEXT_PUBLIC_SITECORE_SITE_NAME=<your Site name from the package>
    NEXT_PUBLIC_SITECORE_API_HOST=https://xmcloudcm.localhost
    NEXT_PUBLIC_SITECORE_API_KEY=<your API key copied from the Docker container>
    JSS_EDITING_SECRET=<your JSS Editing Secret copied from the Docker container>
    DISABLE_SSG_FETCH=true

    Then, in the terminal, run:
    npm install
    npm run start:connected

      Test your app in your browser at http://localhost:3000. If you did it correctly you should see a white page with the Page Content component you created and packaged previously.

      Connect the Pages Editor to your local environment

      Finally, we want to be able to edit our content and test our components in our local environment before we push and deploy anything. To do that, we need to set up Pages to use our local editing host.

      Go to https://pages.sitecorecloud.io/ and log in, then select your site. By default, you’re editing in your XM Cloud environment. Click the Default Editing Host dropdown and change it to localhost:

      Afterward, you will see an error like this:

      There’s one more step. We need to tell Pages to use our local CM as well. Open your browser tools (F12) and in the Application tab, add this key to local storage:
      Sitecore.Pages.LocalXmCloudUrl : https://xmcloudcm.localhost

      After you do this, you should see your content rendering with the Content SDK and be able to edit the page.

      Content SDK in Docker

      This post does not cover is getting the Content SDK host running in a docker container. That isn’t strictly necessary to work locally with the Content SDK. However, if you’d prefer to replace the JSS rendering host with the Content SDK one, that will be covered in a later post.

      Real-time Search Updates with Experience Edge Webhooks: Part 1

      In a previous post, we went over how to use GraphQL and a custom Next.js web service to crawl and index our Sitecore XM Cloud content into a search provider. That crawler runs on a schedule, so what happens when your authors update their content? They’ll need to wait for the next run of the crawler to see their content in the search index. This is a step back in capabilities from legacy Sitecore XP, which updated indexes at the end of every publish.

      It’s possible to recreate this functionality using Experience Edge webhooks. Experience Edge offers quite a few webhook options (see the list here). To enable near real-time updates of our search index, we’ll use the ContentUpdated webhook, which fires after a publish to Edge from XM Cloud finishes. Let’s take a look at an example payload from that webhook:

      {
      "invocation_id": "56a95d51-b2af-496d-9bf1-a4f7eea5a7cf",
      "updates": [
      {
      "identifier": "FA27B51EE4394CBB89F8F451B13FF9DC",
      "entity_definition": "Item",
      "operation": "Update",
      "entity_culture": "en"
      },
      {
      "identifier": "80471F77728345D4859E0FD004F42FEB",
      "entity_definition": "Item",
      "operation": "Update",
      "entity_culture": "en"
      },
      {
      "identifier": "80471F77728345D4859E0FD004F42FEB-layout",
      "entity_definition": "LayoutData",
      "operation": "Update",
      "entity_culture": "en"
      },
      {
      "identifier": "7C77981F5CE04246A98BF4A95279CBFB",
      "entity_definition": "Item",
      "operation": "Update",
      "entity_culture": "en"
      },
      {
      "identifier": "FFF8F4010B2646AF8804BA39EBEE8E83-layout",
      "entity_definition": "LayoutData",
      "operation": "Update",
      "entity_culture": "en"
      }
      ],
      "continues": false
      }

      As you can see, we have item data here and layout data. The layout data is what we’re interested in, as this represents our actual web pages, and that is what we want to index.

      The general process is as follows:

      1. Set up a receiver for this webhook. We’ll do this with a Next.js function.
      2. Loop over the webhook payload and for each piece of LayoutData, then make a GraphQL query to get the field data from Experience Edge.
      3. Finally, roll up the field data into a JSON object and push it to our search index.

      Let’s start by setting up our webhook. You’ll need to create an Edge administration credential in the XM Cloud Deploy app. Make note of the Client ID and Client Secret. The secret will only be displayed once, so if you lose it you will need to create new credentials.

      The next step is to create an auth token, you’ll need this to perform any Experience Edge administration actions. I used the ThunderClient plugin for Visual Studio Code to interact with the Sitecore APIs. To create an auth token, make a post request to https://auth.sitecorecloud.io/oauth/token with the following form data, using the client id and secret you just created in XM Cloud:

      You’ll get back a json object containing an access token. This token is needed to be sent along with any API requests to Experience Edge. This token is passed as a Bearer Token in the Auth header. We can test it with a simple GET request that will list all the webhooks in this Edge tenant.

      You should get back a json object containing a list of all the webhooks currently set up in your tenant (which is likely none to begin). The auth tokens expire after a day or so. If you get a message like edge.cdn.4:JWT Verification failed in your response, you have a problem with your token and should generate a new one.

      Next let’s create our ContentUpdated webhook. You’ll need something to receive the webhook. Since we haven’t created our function in Next.js yet, we can use a testing service like Webhook.site. Create a POST request to https://edge.sitecorecloud.io/api/admin/v1/webhooks with the following body:

      The important parameters here are uri and executionMode. The uri is where the webhook will be sent, in this case our testing endpoint at webhook.site. The execution mode OnUpdate indicates this will fire when content is Updated. (Note: There are separate webhooks for create and delete, which you will probably need to set up later following this same pattern.)

      Send this request and you’ll get a response that looks like this:

      {
      "id": "3cc79139-294a-449e-9366-46bc629ffddc",
      "tenantId": "myTenantName2157-xmcloudvani7a73-dev-2bda",
      "label": "OnUpdate Webhook Sandbox",
      "uri": "https://webhook.site/#!/view/d4ebda52-f7d8-4ae6-9ea2-968c40bc7f2f",
      "method": "POST",
      "headers": {
      "x-acme": "ContentUpdated"
      },
      "body": "",
      "createdBy": "CMS",
      "created": "2024-04-03T15:42:43.079003+00:00",
      "bodyInclude": null,
      "executionMode": "OnUpdate"
      }

      Try your GET request again on https://edge.sitecorecloud.io/api/admin/v1/webhooks, and you should see your webhook returned in the json response.

      Try making some content updates and publishing from XM Cloud. Over at webhook.site, wait a few minutes and make sure you’re getting the json payload sent over. If so, you’ve set this up correctly.

      To delete this webhook, you can send a DELETE request to https://edge.sitecorecloud.io/api/admin/v1/webhooks/<your-webhook-id>. Make sure you include your auth bearer token!

      In the next post, we’ll go over handling this webhook to push content updates into our search index.