Issue with query string not returning results in Sanity client but working in Sanity Studio Vision

23 replies
Last updated: Jun 23, 2023
hello! the query string
 *[_type == "page" && upper(market) == upper($market)
    &&  _id == *[_type == "generalSettings" && upper(market) == upper($market)][0].home->_id
    ]
works correctly inside of sanity studio vision, but when fetching using the sanity client in-app with the exact same string, there are zero results. seems to be stemming from the nested

 _id == *[_type == "generalSettings" && upper(market) == upper($market)][0].home->_id
AI Update

This is a classic perspective mismatch between Vision and your client! The issue is that Vision and your Sanity client are likely using different default perspectives.

The Root Cause

Vision uses the raw perspective by default, which returns ALL documents including both drafts (with drafts. prefix) and published documents. However, as of the API version 2025-02-19, the Sanity client now defaults to the published perspective, which only returns published documents without the drafts. prefix.

When your nested query *[_type == "generalSettings" && upper(market) == upper($market)][0].home->_id runs in Vision, it might be finding a draft version of your generalSettings document (like drafts.generalSettings-123). But when the same query runs through your client with the published perspective, that draft doesn't exist, so the nested query returns nothing, and your comparison fails.

The Solution

You have a few options to fix this:

  1. Set your client to use previewDrafts perspective (recommended for preview/development):
import {createClient} from '@sanity/client'

const client = createClient({
  projectId: 'your-project-id',
  dataset: 'your-dataset',
  perspective: 'previewDrafts', // This prioritizes drafts over published
  useCdn: false, // CDN doesn't support drafts
  apiVersion: '2025-02-19'
})
  1. Use the raw perspective (if you need to see everything):
const client = createClient({
  // ... other config
  perspective: 'raw',
  useCdn: false
})
  1. Ensure your documents are published - If this is for production, make sure both your page and generalSettings documents are actually published (not just drafts).

Understanding the Behavior

The perspectives feature provides different views of your content:

  • raw - Returns all documents (drafts and published)
  • published - Returns only published documents
  • previewDrafts - Prioritizes drafts when they exist, falls back to published

When you dereference with ->, it respects the current perspective. So if you're in published perspective and reference a draft-only document, the dereference returns null, breaking your _id comparison.

Quick Debug Check

To verify this is the issue, temporarily set perspective: 'raw' in your client config. If your query suddenly works, you've confirmed the perspective mismatch!

Hi
user U
. Two things I would check are (1) is Vision returning drafts, which you wouldn’t see in the client by default (unless it’s an authenticated request), and (2) are you using the same API versions in both (essentially, not
v1
)?
Hey
user A
Thanks so much, for real! Vision is not returning drafts, and the api version in both cases is v2
Sorry, in this case the API I’m referring to is for GROQ. Anything after
v1
will be date-based—so something like
v2023-06-22
.
as far as I can tell, if i set all api versions in-app to 2023-06-22, and set my env var for api_version to 2023-06-22.
then run sanity studio, I have both 2021-10-21, and 2021-03-25 versions available. If i select and hit other, and put in 2023-06-22, I will get the same results.

But my api version now should be 2023-06-22 everywhere, and I still get the same results, working in sanity studio vision, not working with the client.
Thanks for confirming.
v2021-03-25
and
v2021-10-21
were both significant releases, which is why they’re included as defaults, but I’d expect both would give the same results as
v2023-06-22
given the query above.
Given that you’re still seeing different results between the client and Vision, can we perhaps see a bit more context of how you’re querying in the client? I.e., the whole
client.fetch
including params.
Course! Thanks so much. Very much appreciated.

// Takes `en-US` and returns `US`
export function getMarketFromNextLocale(locale: string) {
  return locale.split(`-`).pop().toUpperCase()
}

// Takes `en-US` and returns `en`
export function getLanguageFromNextLocale(locale: string) {
  return locale.split(`-`).shift()
}

export default async function PageSlugRoute({ params }) {
  const isDraftMode = draftMode().isEnabled
  let token = null
  // : PostsProps['data']
  if (isDraftMode) {
    token = env.SANITY_STUDIO_API_READ_TOKEN
    if (!token) {
      throw new TypeError(`Missing SANITY_STUDIO_API_READ_TOKEN`)
    }
  }

  // // These query params are used to power this preview
  // // And fed into <Alert /> to create :sparkles: DYNAMIC :sparkles: params!

  // const pageData = await getPage({ slug: params.slug.join('/') })
  const pageData = await getPage({
    //   // Necessary to query for the right page
    // And used by the preview route to redirect back to it
    slug: params.slug.join('/'),
    // slug: params.slug,
    // This demo uses a "market" field to separate documents
    // So that content does not leak between markets, we always include it in the query
    market: getMarketFromNextLocale(params.locale) ?? `US`,
    // Only markets with more than one language are likely to have a language field value
    language: getLanguageFromNextLocale(params.locale) ?? null,
    // In preview mode we can set the audience
    // In production this should be set in a session cookie
    // audience:
    //   isDraftMode && previewData?.audience
    //     ? previewData?.audience
    //     : Math.round(Math.random()),
    // Some Page Builder blocks are set to display only on specific times
    // In preview mode, we can set this to preview the page as if it were a different time
    // By default, set `null` here and the query will use GROQ's cache-friendly `now()` function
    // Do not pass a dynamic value like `new Date()` as it will uniquely cache every request!

    // date: isDraftMode && previewData?.date ? previewData.date : null,
    date: null,
  })

  console.log({ pageData })

  if (!pageData) {
    notFound()
  }

  if (isDraftMode)
    return (
      <Suspense
        fallback={
          <PreviewWrapper>
            <Page data={pageData} />
          </PreviewWrapper>
        }
      >
        <PagePreview token={token} slug={params.slug} />
      </Suspense>
    )

  return <Page data={pageData} />
}
export async function getStaticPage({
  token,
  pageData,
  market,
  language,
}: {
  token?: string | null
  pageData: string
  market?: string
  language?: string | null
}): Promise<undefined> {
  const query = `
  {
    "page": ${pageData},
    ${<http://queries.site|queries.site>}
  }
  `

  return await sanityClient(token)?.fetch(query, { market, language })
}
very confused
another example: working in studio, but not in the client.
*[_id == *[_type == "generalSettings" && upper(market) == upper($market)][0].home->_id]

Maybe params aren’t being passed in correctly? Can you try hardcoding
$market
in the above (in your client) to see if that enables the query to work? By this, I mean that if your
market
param has a value of “spain”, try this query in the client:

*[_id == *[_type == "generalSettings" && upper(market) == upper("spain")][0].home->_id]
Thanks Geoff, but using the hardcoded "CA", or "US" market shows up results in vision, but not using the client. The id's that show in sanity are not drafts
any way I could give you the projectId and you could try the client?
Thanks for confirming. Yeah, please do. I can’t promise anything today but would like to take a look.
im6aa3u9
You say this query works in Vision? I can’t get results for the inner query (
*[_type == "generalSettings" && upper(market) == upper($market)][0]
) whether I replace
$market
with “ca” or “us” .
yup
*[_id == *[_type == "generalSettings" && upper(market) == upper("us")][0].home->_id]
Can you post the JSON you get back?
_updatedAt:2023-06-21T16:27:38Z
slug:{…} 2 properties
current:us-home
_type:slug
hasTransparentHeader:false
_createdAt:2023-06-21T16:26:55Z
_id:9ad39139-13ad-43f1-afc2-e53c8e90ed1c
title:us-home
modules:[…] 1 item
0:{…} 4 properties
bgType:photo
_type:module.hull-hero
_key:b78f3bba9e30
content:[…] 1 item
market:US
_rev:GiJdqoraHD7aGJASGatGZZ
_type:page
language:en
and then using just the inner query
*[_type == "generalSettings" && upper(market) == upper("us")][0]
returns

{…} 7 properties
market:US
_createdAt:2023-06-21T16:27:24Z
_rev:b2a8a5e3-10f0-4d56-80cf-c0a4afaa6fee
_type:generalSettings
_id:drafts.us-generalSettings
_updatedAt:2023-06-21T16:27:28Z
home:{…} 4 properties
_strengthenOnPublish:{…} 1 property
type:page
_weak:true
_ref:9ad39139-13ad-43f1-afc2-e53c8e90ed1c
_type:reference
omg
its a draft
ty for your help lol
You’re welcome! Glad you got it worked out!

Sanity – Build the way you think, not the way your CMS thinks

Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.

Was this answer helpful?