How to fetch draft or published document (whichever is displayed) in GROQ?

6 replies
Last updated: Aug 20, 2020
Hi! one quick question:With GROQ, how can I write a query that fetches the "displayed" document? By "displayed" I mean either the draft (if there's one) or the published document (if there's no draft).
AI Update

Great question! This is a common need when working with Sanity's draft/published workflow. The recommended approach is to use Sanity's built-in perspectives feature, specifically the previewDrafts perspective.

When you configure your Sanity client with perspective: 'previewDrafts', the API automatically returns draft documents when they exist and falls back to published documents when there's no draft:

const client = createClient({
  // ... other config
  perspective: 'previewDrafts',
  useCdn: false // Important: CDN doesn't support perspectives
})

Then your GROQ query can be straightforward:

*[_type == 'post' && slug.current == "my-slug"][0]

The previewDrafts perspective handles all the draft-or-published logic for you automatically. This is particularly useful for preview environments where editors want to see their unpublished changes.

Alternative: Manual GROQ Pattern

If you need to handle this logic directly in your GROQ query (for example, if you can't configure the client perspective), you can use the score() function:

*[_type == 'post' && slug.current == "my-slug"]
  | score(_id in path("drafts.**"))
  | order(_score desc)[0]

This works by:

  1. Filtering for your document(s) as usual
  2. Using score() to assign a higher score to draft documents (those with IDs matching the drafts.** path pattern)
  3. Sorting by score in descending order (drafts first)
  4. Taking the first result [0]

So if a draft exists, it comes first; otherwise, the published version is returned.

Why This Matters

Remember that in Sanity, draft documents have IDs prefixed with drafts. (e.g., drafts.post-123), while published documents use the base ID (e.g., post-123). Without one of these approaches, a basic query might return both versions of the same document, which is usually not what you want.

For most use cases, I'd recommend using the previewDrafts perspective as it's cleaner and more maintainable!

Show original thread
6 replies
Morning! This might depend on your main query, but how about something like this?
*[_type == "post" && slug.current == "my-post"][0]{
  ...*[_id in [^._id, "drafts." + ^._id]] | order(_updatedAt desc)[0],
}
Morning! This might depend on your main query, but how about something like this?
*[_type == "post" && slug.current == "my-post"][0]{
  ...*[_id in [^._id, "drafts." + ^._id]] | order(_updatedAt desc)[0],
}
oh nice! Actually I already know the id of the document, so I guess I can simplify the query.
*[_id in [$id, "drafts." + $id]]
✌️
Awesome!
PS: just realized now why you added "order(_updatedAt desc)[0]". Because otherwise we might get the published document instead of the draft.

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?