Step-by-Step Guide: Sitecore Headless GraphQL Filtering, Sorting & Pagination

If you're building a modern web application using Sitecore Headless (JSS), you’ll often need to fetch dynamic content lists like employee directories, articles, or products.

  • To make this dynamic and user-friendly, you’ll want:
  • Filtering – Narrow down results (e.g. by department or name)
  • Sorting – Order by fields like name, date, or custom rank
  • Pagination – Load data page by page

In this post, I will walk you through how to build a Sitecore GraphQL query step by step, from basic retrieval to fully paginated, sorted, and filtered results.

Demo Setup: Sitecore Content for Querying

To demonstrate these GraphQL features, we’ve prepared the following in Sitecore.

Template: UserInformation

A custom template named UserInformation was created with 5 fields:

Field Name

Field Type

UserName

Single-Line Text

Designation

Single-Line Text

Department

Single-Line Text

Job Description

Multi-Line Text

Rank

Number


Content Tree Structure

Under the Home item, we created a folder called UsersList and added 10 users based on the above template.

/sitecore

└── content

    └── Home

        └── UsersList

            ── User 1

            ── User 2

            ── ...

            └── User 10

IDs for Querying

_path (Root item): {32F38F49-8972-4236-8D81-EF1FC67FFC0D}

_templates (UserInformation): {FD29E0CA-4E3D-479C-8DFE-01A1B7F58DC9}


Step-by-Step GraphQL Query Construction

Step 1: Fetch Items from a Specific Path

{
   search(
    where: {
         name: "_path", value: "{32F38F49-8972-4236-8D81-EF1FC67FFC0D}", operator: CONTAINS 
    }) {
    results{
      id
      ... on UserInformation  {
          UserName: userName {value}
          Designation: designation {value}
          Department: department {value}
          Rank: rank {value}
        }
    }
  }
}

The above query

  • Fetches all child items under the UsersList folder using _path.
  • operator: CONTAINS means the items under it.

Parameter explanation:

  • name: "_path" – Search by the content tree path
  • value – GUID of the parent folder
  • operator – Use CONTAINS to search the children

Step 2: Add Template Filter

{
   search(
    where: {
         AND: [
        { name: "_path", value: "{32F38F49-8972-4236-8D81-EF1FC67FFC0D}", operator: CONTAINS }
        { name: "_templates", value: "{FD29E0CA-4E3D-479C-8DFE-01A1B7F58DC9}", operator: CONTAINS }
      ]
    }
    ) {
    results{
      id
      ... on UserInformation  {
          UserName: userName {value}
          Designation: designation {value}
          Department: department {value}
          Rank: rank {value}
        }
    }
  }
}

The above query

  • Filters results to only include items of type UserInformation using _templates.
  • Use this step when you have many different item types in the same folder.

Parameter explanation:

  • name: "_templates" – Only return items based on this template
  • Use AND to combine multiple filters (path + template)

Step 3: Add Field-Based Filter

  {
   search(
    where: {
         AND: [
        { name: "_path", value: "{32F38F49-8972-4236-8D81-EF1FC67FFC0D}", operator: CONTAINS }
        { name: "_templates", value: "{FD29E0CA-4E3D-479C-8DFE-01A1B7F58DC9}", operator: CONTAINS }
        { name: "Department", value: "Development", operator: CONTAINS }
      ]
    }
    ) {
    results{
      id
      ... on UserInformation  {
          UserName: userName {value}
          Designation: designation {value}
          Department: department {value}
          Rank: rank {value}
        }
    }
  }
}

The above query

  • Adds a field-level filter: only return users in the "Development" department.
  • Useful when implementing search filters in UI (e.g., filter by department, role, etc.)

Parameter explanation:

  • name: "Department" – Name of the field
  • value: "Development" – Field value to match
  • operator: CONTAINS – Partial match; use EQ for exact match

Step 4: Add Sorting (Descending by Rank)

{
   search(
    where: {
         AND: [
        { name: "_path", value: "{32F38F49-8972-4236-8D81-EF1FC67FFC0D}", operator: CONTAINS }
        { name: "_templates", value: "{FD29E0CA-4E3D-479C-8DFE-01A1B7F58DC9}", operator: CONTAINS }
        { name: "Department", value: "Development", operator: CONTAINS }
      ]
    }
    orderBy: 
      {
        name: "Rank"
        direction: DESC
      }
    ) 
   {
    results{
      id
      ... on UserInformation  {
          UserName: userName {value}
          Designation: designation {value}
          Department: department {value}
          Rank: rank {value}
        }
    }
  }
}

The above query

  • Sorts the filtered results by the Rank field in descending order (highest first).

Parameter explanation:

  • orderBy: Accepts one object
  • name: Field name to sort by
  • direction: ASC or DESC

Step 5: Add Pagination

{
   search(
    where: {
         AND: [
        { name: "_path", value: "{32F38F49-8972-4236-8D81-EF1FC67FFC0D}", operator: CONTAINS }
        { name: "_templates", value: "{FD29E0CA-4E3D-479C-8DFE-01A1B7F58DC9}", operator: CONTAINS }
        { name: "Department", value: "Development", operator: CONTAINS }
      ]
    }
    orderBy: 
      {
        name: "Rank"
        direction: DESC
      }
    
     first: 2,                 
     after: ""   
    ) 
   {
    total
    pageInfo {
      endCursor
      hasNext
    }
    results{
      id
      ... on UserInformation  {
          UserName: userName {value}
          Designation: designation {value}
          Department: department {value}
          Rank: rank {value}
        }
    }
  }
}

The above query

  • Limits results to 2 items per page
  • Use endCursor for loading the next page
  • Ideal for UI features like "Load more", pagination buttons, or infinite scrolling.

Parameter explanation:

  • first: Number of items per page to return
  • after: Cursor for pagination to fetch the next page (empty on first page)
  • pageInfo: Metadata for current page
  • pageInfo.endCursor: Use this value in the next query’s after
  • pageInfo.hasNext: true if next page is available based on total items

We now built a fully featured GraphQL query in Sitecore Headless that supports Filtering, Sorting and Pagination.

You can find the complete query on GitHub Gist

Stay Tuned!

Comments

Popular posts from this blog

Setting Up Sitecore Headless with Next.js in Disconnected Mode

Building a Headless Integration Between Sitecore and Azure Functions