Why is GROQ Query Returning an Empty Set in my App but not in the Vision Plugin?
This is a classic perspective issue! The Vision plugin and your app client are likely querying with different perspectives, which means they're looking at different versions of your documents.
What's happening:
Your isUpcoming field might exist on draft documents but not on published ones (or vice versa). When you add the && (!isUpcoming || isUpcoming == null) filter, the query returns nothing because the documents your app is querying don't have that field in the expected state.
Key things to understand:
Draft documents have IDs prefixed with
drafts., so a single exhibition can exist as both a published document (exhibition-123) and a draft (drafts.exhibition-123) simultaneously.Different perspectives show different views:
published- Only published documents (no drafts)drafts(formerlypreviewDrafts) - Prioritizes drafts over published when both existraw- Returns everything (both drafts and published)
Important: With API version 2025-02-19+, the default perspective changed to
published. This means both Vision and your client now default topublishedunless explicitly configured otherwise.
How to diagnose:
First, check what perspective your client is using:
const client = createClient({
// ... other config
perspective: 'published', // Check what's here (or if it's missing)
})Then run this in Vision to see which documents have the isUpcoming field and whether they're drafts or published:
*[_type == "exhibition"]{
_id,
isUpcoming,
"isDraft": _id in path("drafts.**")
}This will show you whether isUpcoming exists on drafts, published docs, or both.
Solutions:
Publish your documents: If
isUpcomingonly exists on drafts, publish those exhibition documents so the field appears in thepublishedperspective.Use the
draftsperspective: If you need to see unpublished changes in your app, configure your client to use thedraftsperspective:const client = createClient({ // ... other config useCdn: false, // must be false when using drafts perspective perspective: 'drafts', })Use the
rawperspective: If you want to see all documents (both drafts and published) like the old default behavior:const client = createClient({ // ... other config perspective: 'raw', })Test with explicit perspective in Vision: Try querying only published docs in Vision to confirm the issue:
*[_type == "exhibition" && !(_id in path("drafts.**")) && (!isUpcoming || isUpcoming == null)]
Most likely, you need to either publish your exhibition documents so isUpcoming appears on published versions, or configure your client to use a perspective that includes the drafts where isUpcoming is actually set. The difference you're seeing is because Vision and your app may be defaulting to different perspectives, or you have unpublished changes that only exist in draft form.
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.