Showing posts with label graphql. Show all posts
Showing posts with label graphql. Show all posts

Thursday, May 14, 2026

XM Cloud Migration and API Patterns — Real Challenges We Faced and How We Fixed Them

Hello Sitecorian Community,

If you’ve worked on a Sitecore XM Cloud migration project, you’ve probably faced something like this:

“The architecture is signed off. The team starts building. Three weeks in, we discover 80% of the existing MVC components won’t work. The estimate needs to triple.”

This is not a rare situation. It happens regularly because most migration guides focus on what to do at a high level — not the specific things that will actually bite you mid-sprint.

In this post, I want to share the real challenges we’ve encountered on XM Cloud migration projects, and the API integration patterns that break in production even when they work fine in dev.

The Real Problem With XM Cloud Migrations

The first thing to be clear about:

Migrating from Sitecore 10 MVC to XM Cloud is not an upgrade. It is a full re-platforming to a headless, SaaS architecture.

Here is what changes at the architecture level:

Traditional Sitecore MVCXM Cloud (Headless)Controller renderings + Razor viewsJSS components in Next.js / ReactServer-side rendering and logicFrontend-driven renderingCD server for content deliveryExperience Edge CDN — no CD serverxDB-driven personalizationRule-based only, or Sitecore PersonalizeTight coupling with .NETAPI-first, decoupled architecture

Challenge 1: Every MVC Rendering Needs to Be Rebuilt

This is consistently the largest effort in any migration.

Controller renderings, Razor views, rendering parameters, server-side visibility logic — all of it needs to become a JSS component. There is no conversion path. You rebuild using the Headless SXA information architecture:

  • Tenant → Site → Pages
  • Page Designs and Partial Designs replace layouts
  • All rendering logic moves to the frontend
  • Custom placeholder logic needs to be redesigned — older nesting patterns don’t map cleanly

⚠️ If your existing solution is not SXA-based: adopting Headless SXA becomes an additional workload on top of the component rebuild. You need to re-map your entire solution into the SXA tenant/site/page structure. Budget for this separately.

Challenge 2: Personalization Cannot Be Migrated — It Needs to Be Redesigned

This is the one that surprises most teams.

XP personalization was powered by xDB analytics. XM Cloud does not have xDB. The personalization rules you built on XP will not work in XM Cloud. Do not try to port them — the engines are fundamentally different.

What we did instead:

  • Audited every existing personalization rule
  • Identified the business intent behind each rule
  • Redesigned using XM Cloud built-in rules for simple cases (geo, auth, device type)
  • Used Sitecore Personalize for anything requiring behavioral targeting or multi-source decisioning

This needs to be a separate scoping conversation with the client — not something discovered during sprint three.

Challenge 3: Bad Content Migrates Perfectly

The tooling (Sitecore CLI, Content Serialization) handles the mechanical migration well. What it cannot fix is content that was already messy.

Common issues we see after migration:

  • Items losing workflow state or associations
  • Media references breaking because folder casing changed
  • Personalize dependencies still embedded in content items
  • Old placeholder configurations causing component mapping errors
  • Inconsistent templates creating template explosion in the new environment

✅ What helped us: Run a full content audit before any migration script runs. Agree a freeze period with the client — no new content during the audit and initial migration window. It sounds disruptive but saves weeks of cleanup later.

Challenge 4: The C# Team and Next.js

Most established Sitecore teams are strong in C#. All XM Cloud documentation and official tooling assumes you are building with Next.js.

The ASP.NET Rendering SDK exists but you will be off the golden path — fewer examples, less community support, and real limitations when integrating with Sitecore Personalize.

What helped us: We ran a Next.js learning sprint before the project kicked off. Getting two or three developers comfortable with React, the JSS SDK, and the Next.js App Router before sprint one paid back immediately. Don’t skip this step.

About SitecoreAI Pathway

Sitecore announced SitecoreAI Pathway at Symposium 2025. Here is what we know from official sources:

  • Handles content migration only — not code. The rendering rebuild is still a separate effort
  • Up to 70% faster content migration timelines — around 100,000 pages migrated during beta
  • Included in Sitecore 360 subscriptions at no extra cost
  • Supports Sitecore XP migrations now, rolling out to Adobe/Optimizely/Contentful
  • AI + human-in-the-loop — you validate and correct what the AI missed

If your client is on Sitecore 360, include a Pathway assessment in your discovery phase.

API Integration — The Patterns That Break in Production

Moving to API integration patterns on Sitecore projects. Here is a scenario we’ve encountered more than once:

“The integration with the CRM worked perfectly in dev and UAT. On go-live day, under real traffic, it starts timing out. Logs point to Experience Edge GraphQL being hammered.”

API failures on Sitecore projects are rarely about writing the wrong code. They are almost always about not fully understanding how Experience Edge works.

How Experience Edge Actually Delivers Content

Experience Edge is Sitecore’s globally distributed, CDN-backed GraphQL API. Your Next.js rendering host fetches content from Edge — not directly from the CM.

Two critical things that follow from this architecture:

  • Custom Content Resolvers do not execute on Edge. They run at publish time only. If your resolver needs runtime context (visitor’s browser, query string, session data) — that is not supported on Edge. Redesign around it.
  • Content not appearing on the live site? Check the publish-to-Edge pipeline first, not your Next.js code. A publishing job that fails silently during busy editorial periods is a common cause — set up infra-level alerting on Edge publish failures.

Caching Strategy — Don’t Treat All Data Sources the Same

The pattern that breaks most often: a page pulls from XM Cloud content, a product API, and CDP profile data — all with the same caching approach. Change frequency is completely different for each source:

Data SourceChange FrequencyCorrect StrategyXM Cloud contentHours to daysISR or full static generation (Next.js)Product / commerce dataMinutes to hoursServer-side with short revalidationCDP profile / audience dataPer sessionClient-side fetch after page load — cannot be edge-cached

The Webhook Write-Back Problem

A common integration pattern now with SitecoreAI: a webhook fires → Azure Function calls an AI service → result writes back to Sitecore via the Management API.

The silent failure we hit: the Management API respects workflow state. If the agent tries to write to an item in an approved or published workflow state, and the API token does not have override permissions — it fails silently. No error in logs. No exception thrown. Just nothing happens.

# Always test write-back against every workflow state:
# Draft state → write allowed ✅
# Awaiting review → depends on token permissions ⚠️
# Approved state → fails silently without override ⚠️
# Published state → fails silently without override ⚠️

✅ Fix: Add Management API write-back tests for every workflow state your implementation uses. Not just the happy path. This test will save you a go-live incident.

Use Sitecore Connect Before Writing Custom Code

Before writing any custom integration from scratch, check Sitecore Connect first. It ships with pre-built connectors for Salesforce, OpenAI, Gemini, and others. Connect integrations operate within SitecoreAI’s governance and audit model. A custom webhook pipeline does not. For enterprise clients with compliance requirements around data flows — this distinction matters.

Why This Helped Our Team

Before understanding these patterns:

  • Go-live issues were hard to diagnose — wrong place to start debugging
  • Multi-source pages had inconsistent behaviour under load
  • Webhook write-backs silently failed in certain workflow states

After:

  • Published pipeline alerts catch Edge failures before users report them
  • Each data source has a caching strategy matched to its change frequency
  • Integration test matrix covers all workflow states explicitly

Final Thoughts

XM Cloud migration and API integration both require understanding the underlying architecture deeply — not just following the official documentation. The documentation tells you what things are. Real project experience tells you where the edges are.

If you are planning an XM Cloud migration, run a full rendering inventory and content audit before committing to a timeline. Those two things will tell you more about the true scope than any architecture review.

Stay tuned for more Sitecore-related articles, tips, and tricks to enhance your Sitecore experience.

Till then, happy Sitecoring! 😊

Please leave your comments or share this article if it’s useful for you!

Wednesday, October 22, 2025

Part 2: Common GraphQL Queries for Sitecore XM Cloud: Advanced Techniques for Powerful Site Building

Hi All,

In the previous blog posts, we covered the fundamentals. Now, let’s delve a bit deeper into the topic.

6. Multi-Level Content Hierarchy Query

When you need to fetch both parent and child items, this nested query is the solution. It’s useful for creating full navigation menus that show both main items and sub-pages.

query ContextItemChildren($contextItem: String!, $language: String!) {
contextItem: item(path: $contextItem, language: $language) {
children {
results {
id
PageTitle: field(name: "PageTitle") {
value: jsonValue
}
children {
results {
id
PageTitle: field(name: "PageTitle") {
value: jsonValue
}
}
}
}
}
}
}

When to use:

  • Building multi-level navigation menus with dropdowns
  • Generating breadcrumb trails with multiple levels
  • Building category/subcategory product listings

Example Variables:

{
"contextItem": "/sitecore/content/MyProject/Home",
"language": "en"
}

7. Template-Specific Content Query

In this query, we’re using fragments to access fields specific to templates. This ensures type safety and allows you to build reusable components for different content types.

query GetTypedContent($itemPath: String!, $language: String!) {
item(path: $itemPath, language: $language) {
id
name
template {
id
name
}
... on Page {
pageTitle: field(name: "PageTitle") {
value
}
metaDescription: field(name: "MetaDescription") {
value
}
}
}
}

When to use:

  • Building reusable queries for different templates
  • Creating generic content components
  • Displaying different fields per content type

Example Variables:

{
"itemPath": "/sitecore/content/MyProject/Home/About",
"language": "en"
}

8. Multi-Template Content Handler Query

This advanced query combines multiple template types in one request using GraphQL fragments. It’s useful when you’re building flexible components that need to handle different content types (like BlogPost, ProductPage, etc.).

query GetContentByType($itemPath: String!, $language: String!) {
item(path: $itemPath, language: $language) {
id
name
template {
id
name
}
... on BlogPost {
title: field(name: "Title") {
value
}
publishDate: field(name: "PublishDate") {
value
}
author: field(name: "Author") {
value
}
}
... on ProductPage {
productName: field(name: "ProductName") {
value
}
price: field(name: "Price") {
value
}
sku: field(name: "SKU") {
value
}
}
... on ContactPage {
address: field(name: "Address") {
value
}
phone: field(name: "Phone") {
value
}
email: field(name: "Email") {
value
}
}
}
}

When to use:

  • Building universal content components
  • Implementing content listings with mixed template types
  • Building search results that adapt to content types

Example Variables:

{
"itemPath": "/sitecore/content/MyProject/Blog/MyBlogPost",
"language": "en"
}

9. Component Datasource Query

When components are using separate content items for data (like a text block or image), this query helps retrieve component-specific data by item ID.

query GetComponentDatasource($datasourceId: String!, $language: String!) {
datasource: item(id: $datasourceId, language: $language) {
id
name
template {
id
name
}
... on TextBlock {
heading: field(name: "Heading") {
value
}
content: field(name: "Content") {
value
}
}
... on ImageContent {
image: field(name: "Image") {
value
jsonValue
}

I hope you enjoy this Sitecore blog. Stay tuned for more Sitecore related articles.

Till that happy Sitecoring :)

Please leave your comments or share this article if it’s useful for you.

Friday, October 17, 2025

Part 1: Common GraphQL Queries for Sitecore XM Cloud: A Developer’s Guide — Basic Queries to Level Up Your Skills

 Introduction

As Sitecore shifts toward headless architecture with XM Cloud, combining it with GraphQL unlocks a world of possibilities for building fast, flexible experiences. GraphQL’s ability to request exactly the data you need is an ideal match for modern frameworks like React, Next.js, and other front-end technologies.

In this blog, we’ll explore some of the most essential GraphQL queries that Sitecore developers use daily. We’ll start simple, focusing on fetching single items, and move to more advanced patterns for handling lists, navigation, and multilingual content. These queries have been production-tested, ensuring that they’re ready to be used in your own projects.

1. Get Item by Path Query

This is one of the simplest queries in Sitecore GraphQL. It’s useful when you know the exact content tree path of an item and want to retrieve its data.

query GetItemByPath($path: String!, $language: String!) {
item(path: $path, language: $language) {
id
name
displayName
path
template {
id
name
}
fields {
name
value
}
}
}

When to use:

  • Building navigation menus
  • Fetching page content by route
  • Loading specific content items

Example Variables:

{
"path": "/sitecore/content/MyProject/Home",
"language": "en"
}

2. Get Item by ID Query

If you have an item GUID, using an ID-based query is more efficient than a path-based query. Since GUIDs are unique across the entire Sitecore instance, this method ensures you’re targeting the exact item.

query GetItemById($id: String!, $language: String!) {
item(id: $id, language: $language) {
id
name
displayName
path
fields {
name
value
jsonValue
}
}
}

When to use:

  • Retrieving datasource items for components
  • Following field references

Example Variables:

{
"id": "{11111111-1111-1111-1111-111111111111}",
"language": "en"
}

3. Basic Field Values Query

Accessing individual fields is essential in Sitecore. Here’s a basic example of how to fetch various field types.

query GetItemFields($itemPath: String!, $language: String!) {
item(path: $itemPath, language: $language) {
id
name
fields {
name
value
definition {type}
}
title: field(name: "Title") {
value
definition {type}
}
description: field(name: "Description") {
value
definition {type}
}
}
}

When to use:

  • Retrieving fields like “Title” and “Description”
  • Displaying text fields

Example Variables:

{
"itemPath": "/sitecore/content/MyProject/Home",
"language": "en"
}

4. Structured Field Data Query

This query is useful for fields with more complex structures, such as images, links, or rich text content.

query GetStructuredFields($itemPath: String!, $language: String!) {
item(path: $itemPath, language: $language) {
heroImage: field(name: "HeroImage") {
value
jsonValue
}
ctaLink: field(name: "CTALink") {
value
jsonValue
}
bodyContent: field(name: "BodyContent") {
value
}
}
}

When to use:

  • Building components that need image dimensions and alt text
  • Creating buttons/links with proper href, text, and target attributes
  • Displaying rich text content with HTML formatting

Example Variables:

{
"itemPath": "/sitecore/content/MyProject/Home",
"language": "en"
}

5. Child Items Navigation Query

This query fetches all child items of a specified parent and is commonly used for building navigation menus, content listings, or displaying child pages under a parent.

query ContextItemChildren($contextItem: String!, $language: String!) {
contextItem: item(path: $contextItem, language: $language) {
children{
results{
id
PageTitle: field(name: "PageTitle") {
value: jsonValue
}
}
}
}
}

When to use:

  • Creating breadcrumb navigation
  • Generating sitemaps or content hierarchies
  • Building folder-based content listings

Example Variables:

{
"contextItem": "/sitecore/content/MyProject/Home",
"language": "en"
}

I hope you enjoy this Sitecore blog. Stay tuned for more Sitecore related articles.

Till that happy Sitecoring :)

Please leave your comments or share this article if it’s useful for you.

Monday, September 29, 2025

Mastering Advanced Search Queries with GraphQL in Sitecore XM Cloud - Part 3

Hi Sitecore folks!,

As your Sitecore content ecosystem evolves, the need for advanced and flexible search queries becomes paramount. Simple GraphQL queries are great for small datasets, but as the content grows and becomes more complex, so too must your queries. This article explores advanced search querying techniques, pagination, and real-world examples that you can implement in Sitecore XM Cloud to optimize content discovery and boost user experience.

Why Advanced Search Queries Matter

When working with Sitecore, especially in large-scale projects, simple queries quickly hit their limits. Advanced querying is essential for handling:

  • Multiple Conditions: Using logical operators like AND/OR for more granular control.
  • Exclusion Filters: Omitting certain results based on specific criteria.
  • Sorting and Pagination: Managing large datasets for better performance and usability.
  • Filtering on Complex or Nested Fields: Targeting multi-value or deeply nested fields.

GraphQL offers the flexibility needed to tackle these challenges efficiently, enabling developers to create precise, high-performing queries that meet both functional and performance requirements.

Tips for Writing Efficient GraphQL Queries

To get the most out of your GraphQL queries in Sitecore, consider the following best practices:

  1. Use Logical Operators (AND/OR/NEQ): Combine conditions effectively for highly specific filtering.
  2. Implement Pagination: Always paginate large result sets to avoid unnecessary data loading and to improve user experience.
  3. Sort Results for Better UX: Sort results based on fields like date or relevance to enhance how users interact with your data.
  4. Test Queries Before Integration: Use tools like GraphQL Playground to test and refine your queries, ensuring they work as expected before going live.

Common Comparison Operators

Understanding how and when to use comparison operators is key to building effective queries. Here’s a quick reference:

Operator Meaning Example Use Case

    EQ Equal to Department = "Engineering"

    NEQ Not equal to Status != "Inactive" 

    GT Greater than HireDate > "2022-01-01"

    LT Less than Salary < 50000 

    GTE Greater or equal Experience >= 5 

    LTE Less or equal Age <= 30

    IN In a list Department IN ("Engineering", "Marketing")

Practical Query Examples

Let’s explore some advanced query examples that leverage these techniques:

1. Filtering by Path, Template ID, and User Name with Pagination

In this example, we query for filtered items based on path, template ID, and user name, with pagination for improved performance.

query FilteredItemsResult(
$pageSize: Int = 10
$after: String
$rootItem: String!
$filterValue: String!
$templateId: String!
) {
ExpertData: search(
where: {
AND: [
{ name: "_path", value: $rootItem, operator: CONTAINS }
{ name: "_templates", value: $templateId, operator: EQ }
{ name: "UserName", value: $filterValue, operator: CONTAINS }
]
}
first: $pageSize
after: $after
) {
pageInfo {
endCursor
hasNext
}
results {
name
id
UserName: field(name: "UserName") { value }
}
}
}

Key Points:

  • AND Grouping: All conditions must be met.
  • Cursor-Based Pagination: Handles large datasets efficiently.

2. Advanced Event Query: Multi-Criteria Filtering

Here we combine OR filters within an AND block to find events based on location, available seats, sponsorship status, and rating. We also exclude events tagged as “Internal” or organized by “Test Org”.

query {
search(
where: {
AND: [
{ name: "_templates", value: "Event", operator: EQ }
{ name: "Status", value: "Published", operator: EQ }
{ name: "StartDate", value: "2025-01-01", operator: GTE }
{ name: "EndDate", value: "2025-12-31", operator: LTE }
{
OR: [
{ name: "City", value: "London", operator: EQ }
{ name: "City", value: "Berlin", operator: EQ }
]
}
{ name: "SeatsAvailable", value: "1", operator: GTE }
{ name: "IsVirtual", value: "false", operator: EQ }
{
OR: [
{ name: "IsSponsored", value: "true", operator: EQ }
{ name: "Rating", value: "4.5", operator: GT }
]
}
{ name: "Tags", value: "Internal", operator: NEQ }
{ name: "Organizer", value: "Test Org", operator: NEQ }
]
}
orderBy: [
{ fieldName: "StartDate", direction: ASC }
{ fieldName: "Rating", direction: DESC }
]
first: 10
) {
total
results {
items {
id
name
fields {
Title { value }
City { value }
StartDate { value }
SeatsAvailable { value }
IsVirtual { value }
IsSponsored { value }
Rating { value }
Tags { value }
Organizer { value }
}
}
pageInfo { endCursor hasNextPage }
}
}
}

3. Combining Filters for Tags and Authors

This query finds articles or news posts tagged with specific labels and authored by designated individuals, all sorted by the most recent publish date.

query {
search(
where: {
AND: [
{
OR: [
{ name: "Tags", value: "News", operator: CONTAINS }
{ name: "Tags", value: "Article", operator: CONTAINS }
]
}
{
OR: [
{ name: "Author", value: "Alice", operator: EQ }
{ name: "Author", value: "Bob", operator: EQ }
]
}
]
}
orderBy: [{ fieldName: "PublishDate", direction: DESC }]
first: 6
) {
total
results {
items {
id
name
fields {
Title { value }
Author { value }
Tags { value }
}
}
pageInfo { endCursor hasNextPage }
}
}
}

Key Points:

  • Nested OR within AND: Complex filtering allows for a flexible and tailored query.
  • Ordering by Date: Ensures the most recent articles appear first.

4. Filtering by Values Greater Than a Specified Amount

This query filters employees by department, hire date, and template, returning a list of employees hired after January 1, 2022, from either the Engineering or Marketing department.

query {
search(
where: {
AND: [
{ name: "_templates", value: "Employee", operator: EQ }
{ name: "Department", value: "Engineering;Marketing", operator: IN }
{ name: "HireDate", value: "2022-01-01", operator: GT }
]
}
orderBy: [
{ fieldName: "Department", direction: ASC }
{ fieldName: "HireDate", direction: DESC }
]
first: 12
) {
total
results {
items {
id
name
fields {
FirstName { value }
LastName { value }
Department { value }
HireDate { value }
Manager {
value
}
}
}
pageInfo { endCursor hasNextPage }
}
}
}

Conclusion

Advanced GraphQL queries provide Sitecore developers with the flexibility to handle complex search scenarios, enabling personalized content discovery and more efficient content management. By mastering pagination, logical operators, and filters, you can create powerful, scalable solutions that enhance user experience while improving performance.

I hope you enjoy this Sitecore blog. Stay tuned for more Sitecore related articles.

Till that happy Sitecoring :)

Please leave your comments or share this article if it’s useful for you.

Thursday, September 25, 2025

Unlocking the Power of GraphQL in Sitecore: A Practical Guide to Queries, Mutations, and Fragments – Part 2

Hi Sitecore folks!,
Welcome back, fellow developers! In the previous post of our GraphQL series, we dived into the basics of querying for component data in Sitecore. If you missed it, be sure to check out [Unlocking the Power of GraphQL in Sitecore: A Guide to Useful Queries – Part 1].

In this second installment, we're going to take a deeper dive into some of the more advanced features of GraphQL: mutations, fragments, and some common queries. These powerful tools allow you to extend your GraphQL usage in Sitecore, making your development work more efficient and dynamic.

We'll walk through how to harness these features, explore real-life examples, and explain how they can streamline your content management workflows. Whether you’re new to GraphQL or a seasoned pro looking to sharpen your skills, this guide will provide you with the essentials to elevate your GraphQL game.

GraphQL Mutations: Modifying Data on the Server

GraphQL isn't just a tool for reading data—it also allows you to modify data via mutations. In Sitecore, mutations empower you to create, update, or delete items, templates, media, and more directly through GraphQL queries.

Unlike REST, which typically uses GET requests to retrieve data, GraphQL uses mutations to make changes to data. This distinction ensures that your data-modifying operations are organized and explicit, reducing the potential for confusion or errors.

Create Data with Mutations

Let's start by exploring a mutation for creating new items. This is useful when you're working with content that's frequently updated or needs to be programmatically generated. Here’s an example mutation for creating a new item in Sitecore:

mutation CreateItem { createItem( name: "Created MutationsItem" template: "{4330C04F-5CAA-4E93-A2B2-634900395E51}" parent: "{C081F940-3F38-4E10-8F43-B80AA63BC1BA}" language: "en" fields: [ { name: "title", value: "New Item Created with Mutations" } { name: "subtitle", value: "This is a new Sitecore item created using mutations." } ] ) { path id } }

This mutation will create a new item with a specific template, assign it to a parent item, and populate its fields (e.g., title and subtitle). The result will return the path and ID of the newly created item.

Update Data with Mutations

Next, let’s look at a mutation for updating an existing item. For example, if you need to update an item's title and subtitle, the mutation would look like this:

mutation UpdateItem { updateItem( path: "/sitecore/content/Home/MySampleProject/MutationsItem/CreatedMutationsItem" language: "en" version: 1 fields: [ { name: "title", value: "Updated Item Title" } { name: "subtitle", value: "Updated subtitle content for the item." } ] ) { title { value } subtitle { value } } }

This mutation targets a specific item and updates its fields with the new values. The query also returns the updated title and subtitle fields.

Delete Data with Mutations

Mutations aren't just for creating and updating—they’re also powerful for deleting content. Here's an example of a mutation that deletes an item:

mutation DeleteItem { deleteItem( path: "/sitecore/content/Home/MySampleProject/MutationsItem/CreatedMutationsItem" ) }

Once executed, the specified item is removed from Sitecore’s content tree.

Fragments: Modularizing Queries for Reusability

As your queries become more complex, you may notice a lot of repetition in your field selections. This is where fragments come into play. A fragment allows you to define a set of fields once and reuse them across multiple queries. This makes your queries more modular, cleaner, and easier to maintain.

In Sitecore, you might be dealing with nested content types, so creating reusable fragments for fields like title and subtitle is incredibly helpful.

Here's an example where we define two fragments: RelatedItems and SubRelatedItems. Each fragment specifies the fields we need for those content types:

fragment RelatedItems on RelatedItems { title { value } subtitle { value } } fragment SubRelatedItems on SubRelatedItems { title { value } subtitle { value } }

Now, you can use these fragments in your main query to fetch content in a cleaner, more efficient way:

query ContentCardQuery($datasource: String!) { datasource: item(path: $datasource, language: "en") { name newsItems: children( includeTemplateIDs: ["{B5D116DE-BFE8-4A06-A3FE-90C84320ECCB}"] ) { name ...RelatedItems } blogItems: children( includeTemplateIDs: ["{02CDBE64-254B-424B-90C3-E74FDBA02572}"] ) { name ...SubRelatedItems } } }

By using inline fragment spreads (...RelatedItems and ...SubRelatedItems), this query is now much cleaner and more maintainable. The fragments handle the repetitive fields for you, and you only need to call them where necessary.

Fetching and Organizing Data Efficiently

GraphQL shines when it comes to fetching specific data. You can retrieve exactly what you need, reducing unnecessary requests and optimizing your application. Here’s an example where we query multiple data sources under a parent item, and we fetch only the required fields for news and blog items:

query ContentCardQuery($datasource: String!) { datasource: item(path: $datasource, language: "en") { name newsItems: children( includeTemplateIDs: ["{B5D116DE-BFE8-4A06-A3FE-90C84320ECCB}"] ) { name ... on RelatedItems { title { value } subtitle { value } } } blogItems: children( includeTemplateIDs: ["{02CDBE64-254B-424B-90C3-E74FDBA02572}"] ) { name ... on SubRelatedItems { title { value } subtitle { value } } } } }

In this query, we use child item queries to pull news and blog items based on their specific templates. We then use fragments to ensure we fetch only the required fields for each item.

Conclusion: GraphQL’s Full Potential in Sitecore

GraphQL in Sitecore isn’t just a powerful tool for reading data; it offers a comprehensive way to manage and interact with content. By leveraging mutations for data creation, updates, and deletions, along with fragments for cleaner, reusable queries, you can optimize your Sitecore applications and workflows.

In this part of the series, we've covered the essential techniques for handling mutations and fragments, but we’re just scratching the surface. Stay tuned for Part 3, where we’ll dive into complex queries, variables, and directives—all the advanced tools you’ll need to become a GraphQL pro in Sitecore.

Until then, get hands-on with what we've covered, and experiment with mutations and fragments in your own projects. The more you practice, the easier it will get!

I hope you enjoy this Sitecore blog. Stay tuned for more Sitecore related articles.

Till that happy Sitecoring :)

Please leave your comments or share this article if it’s useful for you.

Thursday, September 18, 2025

Mastering GraphQL Queries in Sitecore: A Developer's Practical Playbook - Part 1

Hi All,

In my Previous blog (Supercharging Sitecore with GraphQL: A Developer’s Guide) we started on GraphQL step by step instructions on how to configure and utilize the Sitecore GraphQL API on your local machine.

As developers, we've all been there. You have the power of GraphQL at your fingertips, but finding the right query pattern for your specific use case feels like searching for a needle in a digital haystack. That's exactly why this comprehensive guide exists – to be your go-to reference when you need to craft the perfect GraphQL query for Sitecore.

Setting the Stage: Understanding Your Content Structure

Before diving into queries, let's establish the foundation. Imagine you're working on an enterprise website with the following content structure:

/sitecore/content/Home/MySampleProject/Home/
├── About ├── Services ├── News └── Contact

Each of these items has specific templates with fields like:

  • About Template: Title, Subtitle, Description, Image, Date, IsTrue
  • Services Template: ServiceName, ServiceDescription, Icon, Features (Multilist)
  • News Template: Headline, Summary, Author, PublishDate, RelatedArticles (Treelist)

This realistic structure will ground all our examples in practical, real-world scenarios.

The Essential Query Patterns Every Sitecore Developer Needs

1. The Foundation Query: Getting All Field Data

Real-world scenario: You're debugging a component and need to see all available field data for an item.

query InspectAllFields($contextItem: String!) {
  item(path: $contextItem) {
    fields {
      name
      value
    }
  }
}

Query Variables:

{
  "contextItem": "/sitecore/content/Home/MySampleProject/Home/About"
}

Why this matters: This is your debugging lifeline. When a component isn't displaying correctly, this query shows you exactly what data is available and how it's structured.

2. The Content Retrieval Pattern: Getting Specific Item Data

Real-world scenario: You're building a hero component that needs specific fields from a content item.

query HeroComponentData {
  item(path: "/sitecore/content/Home/MySampleProject/Home/About") {
id name template { name } subTitle: field(name: "SubTitle") { value } description: field(name: "Description") { value } } }

Developer insight: Notice how we're using aliases (subTitle, description) to make the response more readable and match our frontend component props.

3. The Template Filter Pattern: Type-Safe Content Querying

Real-world scenario: You have a mixed content area, but only want to display specific item types.

query FilteredChildrenByTemplate($contextItem: String!) {
  item(path: $contextItem) {
    children {
      ... on About {
        id
        name
        title: field(name: "Title") {
          value
        }
        subtitle: field(name: "Subtitle") {
          value
        }
      }
      ... on Services {
        id
        name
        serviceName: field(name: "ServiceName") {
          value
        }
        serviceDescription: field(name: "ServiceDescription") {
          value
        }
      }
    }
  }
}

Pro tip: This pattern is incredibly powerful for building dynamic listing components where different item types need different field mappings.

4. The Common Fields Pattern: Flexible Content Components

Real-world scenario: You're building a card component that can display different content types but needs consistent field access.

query FlexibleContentCard($datasource: String!) {
  datasource: item(path: $datasource, language: "en") {
    id
    name
    url
    ... on About {
      title {
        value
      }
      subtitle {
        value
      }
      image {
        alt
        src
      }
      link {
        url
      }
      date {
        value
      }
      isTrue {
        value
      }
    }
    ... on News {
      headline: title {
        value
      }
      summary: subtitle {
        value
      }
      publishDate: date {
        value
      }
      author {
        value
      }
    }
  }
}

Developer insight: This pattern allows one component to handle multiple content types gracefully, reducing code duplication.

5. The Relationship Query Pattern: Handling Multilist and Treelist Fields

Real-world scenario: You're building a services page where each service has related case studies or features.

Approach 1: Direct Field Access

query ServiceWithRelatedItems($datasource: String!) {
  datasource: item(path: $datasource, language: "en") {
    id
    name
    ... on Services {
      serviceName: title {
        value
      }
      serviceDescription: subtitle {
        value
      }
      relatedFeatures: multiListItems {
        id
        name
        targetItems {
          id
          name
          path
          ... on Feature {
            featureName: title {
              value
            }
            featureDescription: description {
              value
            }
            icon {
              src
              alt
            }
          }
        }
      }
    }
  }
}

Approach 2: Search-Based Access

query ServiceFeaturesViaSearch {
  search(
    fieldsEqual: [
      {
        name: "_fullpath"
        value: "/sitecore/content/Home/MySampleProject/Home/Services"
} ] keyword: "" ) { results { items { item { ... on Services { serviceName: title { value } relatedFeatures: multiListItems { value name targetItems { id name ... on Feature { featureName: title { value } description { value } } } } } } } } } }

When to use which approach: Use Approach 1 when you know the exact item path. Use Approach 2 when you need to find items dynamically or apply additional search criteria.

6. The DropLink Pattern: Single Reference Fields

Real-world scenario: Your news articles reference a single author item, and you need author details.

query NewsWithAuthor($datasource: String!) {
  datasource: item(path: $datasource, language: "en") {
    id
    name
    ... on News {
      headline: title {
        value
      }
      summary {
        value
      }
      authorReference: dropLinkItem {
        id
        name
        targetItem {
          id
          name
          ... on Author {
            fullName: displayName {
              value
            }
            bio {
              value
            }
            profileImage {
              src
              alt
            }
          }
        }
      }
    }
  }
}

7. The Alias Power Pattern: Clean, Component-Ready Data

Real-world scenario: Your frontend team needs field names that match their component props exactly.

query ComponentReadyData($contextItem: String!) {
  componentData: item(path: $contextItem) {
    componentId: id
    ... on About {
      heading: title {
        value
      }
      subheading: subtitle {
        value
      }
      bodyText: description {
        value
      }
      ctaLink: link {
        url
        target
      }
    }
  }
}

Developer benefit: This eliminates the need for field mapping in your frontend code, making components cleaner and more maintainable.

8. The Hierarchical Data Pattern: Multi-Level Navigation

Real-world scenario: You're building a navigation component that needs to show page hierarchies.

query NavigationStructure {
  item(path: "/sitecore/content/Home/MySampleProject/Home") {
children { name id url template { name } ... on NavigationItem { navigationTitle: title { value } showInNavigation { value } } children { name id url template { name } ... on NavigationItem { navigationTitle: title { value } showInNavigation { value } } # Third level if needed children { name id url ... on NavigationItem { navigationTitle: title { value } } } } } } }

9. The Template-Only Fields Pattern: Clean Data Filtering

Real-world scenario: You want to avoid system fields and only get template-defined fields for a cleaner component interface.

query CleanTemplateFields($contextItem: String!) {
  item(path: $contextItem) {
    fields(ownFields: true) {
      id
      name
      value
      type
    }
    template {
      name
      fields {
        name
        type
      }
    }
  }
}

Why this matters: This prevents your components from accidentally depending on Sitecore system fields, making them more portable and maintainable.

10. The Search Mastery Patterns: Finding Content Dynamically

Pattern A: Exact Field Matching

Real-world scenario: Finding all blog posts by a specific author.

query BlogPostsByAuthor {
  search(
    fieldsEqual: [
      {
        name: "author"
        value: "John Smith"
      }
      {
        name: "_template"
        value: "BlogPost"
      }
    ]
    rootItem: "/sitecore/content/Home/MySampleProject/Home/Blog"
) { results { totalCount items { item { id name path url ... on BlogPost { title { value } publishDate { value } summary { value } author { value } } } } } } }

Pattern B: Keyword Search

Real-world scenario: Building a site search feature.

query SiteSearch($searchTerm: String!, $searchRoot: String!) {
  search(
    keyword: $searchTerm
    rootItem: $searchRoot
  ) {
    results {
      totalCount
      items {
        item {
          id
          name
          path
          url
          template {
            name
          }
          # Get title field regardless of template
          title: field(name: "Title") {
            value
          }
          # Get description field regardless of template
          description: field(name: "Description") {
            value
          }
        }
      }
    }
  }
}

Query Variables:

{
  "searchTerm": "GraphQL",
  "searchRoot": "/sitecore/content/Home/MySampleProject/Home"
}

Pattern C: Complex Field-Based Search

Real-world scenario: Finding products within a specific category and price range.

query ProductSearch {
  search(
    fieldsEqual: [
      {
        name: "Category"
        value: "Electronics"
      }
      {
        name: "InStock"
        value: "1"
      }
    ]
    rootItem: "/sitecore/content/Home/MySampleProject/Home/Products"
) { results { totalCount items { item { id name path url ... on Product { productName: title { value } price { value } category { value } inStock { value } productImage: image { src alt } } } } } } }

Advanced Patterns for Complex Scenarios

Error-Handling Query Pattern

Real-world scenario: You need to ensure your queries gracefully handle missing content.

query SafeContentQuery($contextItem: String!) {
  item(path: $contextItem) {
    id
    name
    template {
      name
    }
    ... on About {
      title {
        value
        ... on TextField {
          value
        }
      }
      # Fallback for missing fields
      subtitle {
        value
      } @skip(if: false)
    }
  }
}

Multi-Language Content Pattern

Real-world scenario: Building a multilingual site with language-specific content.

query MultiLanguageContent($itemPath: String!, $language: String!) {
  item(path: $itemPath, language: $language) {
    id
    name
    language
    ... on About {
      title {
        value
      }
      subtitle {
        value
      }
    }
  }
  
  # Get versions in other languages
  allLanguages: item(path: $itemPath) {
    versions {
      language
      version
    }
  }
}

Performance Optimization Tips from the Trenches

1. Query Only What You Need

# ❌ Avoid: Over-fetching
query BadExample {
  item(path: "/sitecore/content/Home/MySampleProject/Home") {
fields { name value } children { fields { name value } } } } # ✅ Better: Specific field requests query GoodExample { item(path: "/sitecore/content/Home/MySampleProject/Home") {
title { value } description { value } } }

2. Use Fragments for Reusable Patterns

fragment BasicItemInfo on Item {
  id
  name
  path
  url
}

fragment ContentFields on About {
  title {
    value
  }
  subtitle {
    value
  }
  image {
    src
    alt
  }
}

query OptimizedQuery($contextItem: String!) {
  item(path: $contextItem) {
    ...BasicItemInfo
    ...ContentFields
  }
}

Troubleshooting Common Issues

Problem: "Field not found" Errors

Solution: Always check your template inheritance and field names in Sitecore. Use the field inspection query first.

Problem: Empty Results from Search Queries

Solution: Verify your search indexes are up to date and check field value formatting (especially for boolean and date fields).

Problem: Performance Issues with Deep Hierarchies

Solution: Limit nesting levels and use search queries instead of deep children traversals when possible.

Building Your GraphQL Query Library

As you work with these patterns, consider building a team library of commonly used queries. Here's a suggested structure:

/graphql-queries/
├── /components/
│   ├── hero-queries.graphql
│   ├── navigation-queries.graphql
│   └── listing-queries.graphql
├── /search/
│   ├── content-search.graphql
│   └── product-search.graphql
└── /utils/
    ├── field-inspection.graphql
    └── debug-queries.graphql

The Path Forward: From Queries to Solutions

Understanding these query patterns is just the beginning. The real power comes from combining them creatively to solve specific business challenges. Whether you're building a dynamic product catalog, a complex news portal, or an interactive corporate website, these patterns provide the building blocks for success.

Remember, GraphQL in Sitecore isn't just about fetching data – it's about empowering your development team to build more maintainable, performant, and scalable digital experiences. Every query you craft is an investment in your application's future flexibility and your team's productivity.

Your Next Steps

  1. Start with the basics: Master the field inspection and content retrieval patterns
  2. Build incrementally: Add complexity gradually as your comfort level increases
  3. Document your discoveries: Keep notes on query patterns that work well for your specific use cases
  4. Share with your team: Create a shared knowledge base of successful query patterns
  5. Performance test: Always validate that your queries perform well with realistic data volumes

The journey to GraphQL mastery in Sitecore is iterative. Each query you write, each pattern you discover, and each problem you solve adds to your expertise. Your future self – and your entire development team – will thank you for building this foundation of knowledge and practical experience.


Ready to dive deeper? The Sitecore and GraphQL communities are incredibly supportive. Don't hesitate to share your own query discoveries and learn from others who are solving similar challenges.

I hope you enjoy this Sitecore blog. Stay tuned for more Sitecore related articles.

Till that happy Sitecoring :)

Please leave your comments or share this article if it’s useful for you.

Monday, September 8, 2025

Supercharging Sitecore with GraphQL: A Developer’s Guide

Introduction

In the dynamic world of web development, efficiency and performance are paramount. One powerful way to achieve both is by integrating GraphQL with Sitecore, a leading enterprise content management system. While GraphQL isn’t new to Sitecore, the advent of Sitecore JavaScript Services (JSS) has made its implementation smoother and more accessible than ever.

This guide walks you through the process of setting up GraphQL in Sitecore, focusing on how to craft effective queries to retrieve content. Whether you're building a new solution or enhancing an existing one, this integration can be a game-changer.


Why GraphQL with Sitecore?

Integrating GraphQL into Sitecore offers a host of benefits:

  • Enhanced Flexibility: Fetch only the data you need—no more, no less.
  • Streamlined Development: Decouple frontend and backend workflows for faster iteration.
  • Improved Performance: Reduce payload sizes and speed up response times.
  • Modern Frontend Compatibility: GraphQL’s schema-based approach aligns perfectly with frameworks like React, Vue.js, and Angular.

Getting Started: Installing JSS and GraphQL

To begin, you’ll need the correct JSS server component package for your Sitecore version. Once identified:

Step 1: Install the JSS Package

  • Open Sitecore in your browser.
  • Navigate to: Desktop → Development Tools → Installation Wizard.
  • Upload and install the JSS package.

This installation will add several components to your Sitecore instance:

  • Configuration files under App_Config (including LayoutService and GraphQL configs).
  • DLLs in the bin folder.
  • JssApp.cshtml view file.
  • Sitecore templates under Foundation and System.

Step 2: Configure GraphQL

After installation:

  1. Go to App_Config\Services.GraphQL.
  2. Rename the file Sitecore.Services.GraphQL.Content.Master.config.example to Sitecore.Services.GraphQL.Content.Master.config.

Step 3: Add and Configure API Key

To query GraphQL via API:

  1. Switch to the Core database.
  2. Navigate to /sitecore/system/Settings/Services/API Keys.
  3. Right-click and insert a new API Key item.

Enable API Key Authentication

Edit the file:
App_Config\Sitecore\Services.GraphQL\Sitecore.Services.GraphQL.config

In the <security> section, enable API Key authentication.


Step 4: Access the GraphQL Browser

Once everything is set up, you can test your queries using the Sitecore GraphQL Browser:

  • URL format:
    https://your-domain/sitecore/api/graph/items/master/ui

If you encounter an “Authentication required” error:

Fix: Add Site-Neutral Path

  1. Open:
    App_Config\Sitecore\Owin.Authentication\Sitecore.Owin.Authentication.config
  2. Locate the <siteNeutralPaths> node.
  3. Add:
    /sitecore/api/graph/items/

Save the file and retry accessing the GraphQL browser.


Step 5: Run Your First Query

Here’s a sample GraphQL query to fetch child items of a context item:

query GQLQuerySamples($contextItem: String!) {
  item(path: $contextItem) {
    children {
      id
      name
    }
  }
}

Important Notes:

  • In the Query Variables section, pass the contextItem path.
  • In the HTTP Headers, include your API Key.

Conclusion

Integrating GraphQL with Sitecore empowers developers to build faster, leaner, and more scalable applications. With precise data fetching, improved performance, and seamless frontend integration, GraphQL is a natural fit for modern Sitecore solutions.

Whether you're a seasoned Sitecore developer or just getting started, embracing GraphQL can elevate your development workflow and deliver richer digital experiences.

I hope you enjoy this Sitecore blog. Stay tuned for more Sitecore related articles.

Till that happy Sitecoring :)

Please leave your comments or share this article if it’s useful for you.