Composable DXP Development MACH Sitecore

Prefetch Sitecore data with React Query and Sitecore JSS Next.js apps on non-Sitecore pages

With Sitecore’s Composable offerings, you can develop applications in different product stacks instead of only in Sitecore Context.

Sitecore is one composable platform for rich customer experiences, from content to commerce and offers many ways to manage the content so that you can re-use it for different purposes across the delivery channels, e.g., in web applications, mobile applications or kiosks, etc.

While using Sitecore’s true Headless Content Management System capabilities you will be creating Head of your application in NextJS using Sitecore JavaScript Rendering SDK (JSS) for Next.js.

In some cases, it’s not always required to build the website pages/components in Sitecore, and instead Sitecore provides flexibility to write code to any place and get data/content from Sitecore via different endpoints (Sitecore Item Service, Sitecore OData Item Service, Sitecore Layout Service, Sitecore GraphQL).

In this blog, let’s look at how we can use Sitecore GraphQL (GQL) to access Sitecore Content from the non-Sitecore Pages and Component in this article.

Here, I will fetch the data at the page level and will utilize the same data at any component of the page.

Below are the steps for implementing the above-mentioned use case. You can implement/extend functionalities as per your needs, e.g., authentication on Sitecore GraphQL calls, etc. by creating the wrapper.

Code Details

types.ts

import { QueryKey } from "react-query"

export type SitecoreItemDetails = {
  id: string
  name: string
  displayName: string
  parent: {
    id: string
    name: string
    displayName: string
  }
}

export type GetSitecoreItemDetailsRequest = {
  item: SitecoreItemDetails
}

export type getSitecoreItemDetailsQueryParameter = {
  itemIdOrPath: string
}

export type PrefetchSitecoreItemProps = {
  itemIdOrPath: string
}

export const CacheKeys = {
  sitecoreItem(itemIdOrPath: string): QueryKey {
    return ["siteItemDetails", itemIdOrPath]
  },
}

export interface GetSitecoreItemDetails {
  data: SitecoreItemDetails
}

api.ts

import { GraphQLRequestClient } from "@sitecore-jss/sitecore-jss-nextjs"
import { gql } from "graphql-request"
import {
  getSitecoreItemDetailsQueryParameter,
  GetSitecoreItemDetailsRequest,
  SitecoreItemDetails,
} from "./types"

const getSitecoreItemDataGQL = gql`
  query GetSitecoreItemDetails($itemIdOrPath: String = "") {
    item(path: $itemIdOrPath, language: "en") {
      id(format: "B")
      name
      displayName
      parent {
        id(format: "B")
        name
        displayName
      }
    }
  }
`
export const getSitecoreItem = (graphQLClient: GraphQLRequestClient) =>
  async function (
    values: getSitecoreItemDetailsQueryParameter
  ): Promise<SitecoreItemDetails> {
    const itemData = await graphQLClient.request<GetSitecoreItemDetailsRequest>(
      getSitecoreItemDataGQL,
      values
    )
    const item: SitecoreItemDetails = itemData.item
    return item
  }

queries.ts

import { useQuery } from "react-query"
import { QueryClient } from "react-query"


import { graphQLClientFactory } from "lib/graphql-client-factory"
import {
  GetSitecoreItemDetails,
  PrefetchSitecoreItemProps,
  CacheKeys,
} from "./types"
import { getSitecoreItem } from "./api"

export const prefetchSitecoreItem = async (
   { itemIdOrPath }: PrefetchSitecoreItemProps
): Promise<void> => {
  if (!isServerServide()) {
    throw new Error("Sitecore Item Prefetching can not run on client")
  }
  const graphQLClient = new GraphQLRequestClient(config.graphQLEndpoint, {    
    apiKey: config.sitecoreApiKey,  
  });
  const queryClient = new QueryClient()

  await queryClient?.prefetchQuery(
    CacheKeys.sitecoreItem(itemIdOrPath),
    () =>
      getSitecoreItem(graphQLClient)({
        itemIdOrPath: itemIdOrPath,
      })
  )
}


export const useSitecoreItem = ({
  itemIdOrPath,
}: PrefetchSitecoreItemProps): GetSitecoreItemDetails => {
  const graphQLClient = new GraphQLRequestClient(config.graphQLEndpoint, {    
    apiKey: config.sitecoreApiKey,  
  });
    
  const { data } = useQuery(
    CacheKeys.sitecoreItem(itemIdOrPath),
    () =>
      getSitecoreItem(graphQLClient)({
        itemIdOrPath: "",
      }),
    {
      // set to false which prevent this query running automatically for client side fetching of data and will fetch
      // the pre-populated data from cache object
      enabled: false,
    }
  )

  return {
    data: data ?? {
      id: "",
      name: "",
      displayName: "",
      parent: { id: "", name: "", displayName: "" },
    },
  }
}

export const isServerServide = () => {
  return typeof window === "undefined"
}

Details from code

types.ts

This contains all the object’s details used to fill the data.

api.ts

This contains the code related to Sitecore GraphQL queries.

getSitecoreItemDataGQL

You can get the running Sitecore GraphQL query and store it in this variable and use this as a parameter if required.

getSitecoreItem

This function received the GraphQLRequestClient which is required to execute the GraphQL queries and parameter which needs to be passed to the Sitecore GraphQL query defined in the getSitecoreItemDataGQL.

queries.ts

This contains the code related to prefetching data and store in the cache and retrieve from cache.

prefetchSitecoreItem

The purpose of this function is to fetch the data from Sitecore using Sitecore GraphQL (GQL) query and store it in the cache for future use.

This function will be used at the server side code (getServerSideProps) and at the Page level, but not at the client side.

This function will perform the following steps:

  • Creation of the GraphQL Client, in order to execute the GQL queries from the getSitecoreItem function defined in the \api.ts.
  • The parameter itemIdOrPath is the Sitecore Item Id or Sitecore Item Path. We can hardcode this in the constant file (if not going to change) or we can pass via an environment file.
  • Creation of the QueryClient to interact with a cache.
  • The queryClient.prefetchQuery: is an asynchronous method used to prefetch the data so that it can be used later.
await queryClient.prefetchQuery({ queryKey, queryFn })
  • This function will get the Sitecore GraphQL data and store it in the cache based on the cache key CacheKeys.sitecoreItem(itemIdOrPath), and I am creating a cache key with the combination of Sitecore Item Id or Item Path so that it would be unique:
// From: \types.ts
export const CacheKeys = {
  sitecoreItem(itemIdOrPath: string): QueryKey {
    return ["siteItemDetails", itemIdOrPath]
  },
}

useSitecoreItem

The purpose of this function is to fetch the data from the cache stored by the prefetchSitecoreItem function on the basis of the same cache key used in the prefetchSitecoreItem function.

This function would be used at client code or wherever we need to render the content e.g., at the component level.

This function will perform the following steps:

  • Creation of the GraphQL Client, in order to execute the GQL queries from the getSitecoreItem function defined in the \api.ts.
  • The parameter itemIdOrPath is the Sitecore Item Id or Sitecore Item Path. We can hardcode this in the constant file (if not going to change) or we can pass via an environment file.
  • The useQuery used to fetch data from the cache. useQuery accepts two parameter, the first one is the key and the second is the asynchronous function which fetches data from cache because we have set enabled to false to disable this query from automatically running.
  • This function will get the Sitecore GraphQL data stored in the cache based on the cache key CacheKeys.sitecoreItem(itemIdOrPath), used during the prefetch.

We are using prefetching here to load the data to cache from server to improve the performance before client-side code started executed.

The cache in front-end application used to store the important data or costly API calls data. Once data stored in the cache, then calls to API’s can be avoided by combining the prefetch related logic.

If any update at the source or Sitecore Item then whenever the page reloads again then fresh data would be served to user.

Credit/References

Amit is an IT Solution Architect with Assurex. Reach out to Amit on

Did you find this useful? If so, please share with your connections.

Leave a Reply

Your email address will not be published. Required fields are marked *