Using GROQ query result value in another query for filtering

13 replies
Last updated: Jul 15, 2021
What would be the appropriate way to use a value from a groq query in a separate query. for example i'm getting the date of a blog post, and i need another query to get the next post based on published date
AI Update

Based on your use case of getting the next blog post after a certain date, you'll want to use the value from your first query as a parameter in your second query. Here's how to approach this:

Basic Approach

First, fetch the current blog post and extract the date:

const currentPost = await sanityClient.fetch(
  `*[_type == "post" && slug.current == $slug][0]{
    title,
    publishedDate
  }`,
  { slug: "my-post-slug" }
)

Then use that date as a parameter in your second query to get the next post:

const nextPost = await sanityClient.fetch(
  `*[_type == "post" && publishedDate > $date] | order(publishedDate asc)[0]`,
  { date: currentPost.publishedDate }
)

More Efficient: Single Query Approach

However, you can actually accomplish this more efficiently with a single query that gets both the current post and the next post. Here's how:

const result = await sanityClient.fetch(
  `*[_type == "post" && slug.current == $slug][0]{
    title,
    publishedDate,
    "nextPost": *[_type == "post" && publishedDate > ^.publishedDate] | order(publishedDate asc)[0]{
      title,
      slug
    }
  }`,
  { slug: "my-post-slug" }
)

The ^ operator references the parent document's field, so ^.publishedDate refers to the current post's date while you're filtering for the next post.

Key Points

As covered in the GROQ Parameters documentation, parameters are passed using the $ syntax and must be JSON literals. This approach is much safer than string concatenation and prevents GROQ injection attacks.

The single-query approach is generally preferable because it:

  • Reduces API calls (better performance and lower costs)
  • Ensures data consistency (both results from the same point in time)
  • Simplifies your code

If you need to get both the previous and next posts, you can extend the single query:

*[_type == "post" && slug.current == $slug][0]{
  title,
  publishedDate,
  "nextPost": *[_type == "post" && publishedDate > ^.publishedDate] | order(publishedDate asc)[0],
  "previousPost": *[_type == "post" && publishedDate < ^.publishedDate] | order(publishedDate desc)[0]
}
Show original thread
13 replies
If you want it to be a separate query, you could pull the desired field into your next query using query parameters or template literals. However, it could also work inside the same query (if that works in your use case) by using multiple projections. For example, this would return all posts with a date more recent than the post at index 5:

*[_type == 'post'] | order(date desc)[5] {
  date,
} {
  'result': *[_type == 'post' && date > ^.date]
}
ideally yea, i would like all in the same query. what i would really want is to return all the data i currently get for the specific post, along with a slug for the previous and next post
looking into multiple projections
obviously not ideal currently
const slug = route.params.episode;
const query = groq`*[_type == "episode" && slug.current == "${slug}" ][0]{_id, title, "author": author->, slug, sponsor->,  mainImage, episodeURL, episodeTypes->, publishedAt, body }`;
const episode = await $sanity.fetch(query)
const next = groq`*[_type == "episode" && publishedAt > "${await episode.publishedAt}"][0]{slug, title}`
const nextEp = await $sanity.fetch(next);
const prev = groq`*[_type == "episode" && publishedAt < "${await episode.publishedAt}"][0]{slug, title}`
const prevEp = await $sanity.fetch(prev);
obviously not ideal currently
const slug = route.params.episode;
const query = groq`*[_type == "episode" && slug.current == "${slug}" ][0]{_id, title, "author": author->, slug, sponsor->,  mainImage, episodeURL, episodeTypes->, publishedAt, body }`;
const episode = await $sanity.fetch(query)
const next = groq`*[_type == "episode" && publishedAt > "${await episode.publishedAt}"][0]{slug, title}`
const nextEp = await $sanity.fetch(next);
const prev = groq`*[_type == "episode" && publishedAt < "${await episode.publishedAt}"][0]{slug, title}`
const prevEp = await $sanity.fetch(prev);
I’m using
_createdAt
because it uses datetime—which would be pretty necessary for a technique like this—but what about:

*[_type == 'post'] | order(_createdAt desc)[5] {
  'post': @,
  'next': *[_type == 'post' && _createdAt > ^._createdAt] | order(_createdAt asc)[0],
  'prev': *[_type == 'post' && _createdAt < ^._createdAt] | order(_createdAt desc)[0],
}
Though in your case, you would use a projection to just return
slug.current
for
prev
and
next
.
i think i see what you're getting around, going to play around with your query a bit
still pretty new to sanity, but this is helpful
*[_type == 'episode' && slug.current == "${slug}"]{_id, title, "author": author->, slug, sponsor->,  mainImage, episodeURL, episodeTypes->, publishedAt, body } | order(publishedAt desc)[0] {
    'episode': @,
    'next': *[_type == 'episode' && publishedAt > ^.publishedAt]{slug, title, publishedAt} | order(publishedAt asc)[0],
    'prev': *[_type == 'episode' && publishedAt < ^.publishedAt]{slug, title, publishedAt} | order(publishedAt desc)[0],
  }
went with above, i think this works nicely. and man i love the simplicity.
Nice! Glad to hear it, Matt. Happy building!
Nice! Glad to hear it, Matt. Happy building!

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?