Fetching multiple document types with conditional fields in Sanity GROQ query

7 replies
Last updated: Apr 2, 2022
Hello, I am using NextJS with Sanity and trying to fetch data of more than one document type to render on a page. I’m trying to use the below query but at the moment it is not working. Specifically I have a page on my website for events, and on that page I want to fetch ‘calendar’ documents, ‘notice’ documents, and ‘event’ documents.
*[_type in ['event', 'calendar', 'notice']]{
_type == 'event': {
  eventTitle,
  eventDate,
  venue,
  mainImage,
  artists[]->,
},
_type == 'notice': {
  _id,
  title,
  body, 
},
_type == 'calendar': {
  _id,
  month,
  'event': event[]->{
  title[],
  date,
  },
},
}
Grateful for any tips!
🙏
AI Update

I can see the issue with your query! The syntax you're using isn't quite right for conditional projections in GROQ. You're trying to use _type == 'event': { ... } as if it's creating conditional objects, but that's not valid GROQ syntax.

Here's the correct approach using conditional projections with the => operator:

*[_type in ['event', 'calendar', 'notice']]{
  _type,
  _type == 'event' => {
    eventTitle,
    eventDate,
    venue,
    mainImage,
    artists[]->
  },
  _type == 'notice' => {
    _id,
    title,
    body
  },
  _type == 'calendar' => {
    _id,
    month,
    'event': event[]->{
      title,
      date
    }
  }
}

Key differences:

  1. Use => instead of : - The arrow operator (=>) is what makes conditional projections work in GROQ
  2. Include _type in the base projection - This helps you identify which type each document is when you process the results in Next.js
  3. Remove the curly braces around conditions - Each condition should be at the projection level, not wrapped in an object

The way this works: when _type == 'event' is true, those fields get included in the projection. When it's false, they're simply omitted. Each document in your results will only have the fields relevant to its type.

If you want to include common fields for all types plus type-specific ones, you can use the ellipsis operator:

*[_type in ['event', 'calendar', 'notice']]{
  ...,
  _type == 'event' => {
    artists[]->
  }
}

This would include all existing fields from each document, plus expand the artists reference only for event documents. The ellipsis (...) is always evaluated first regardless of position, so it won't override your conditional fields.

Show original thread
7 replies
Ok so changing the
:
to a
=>
made it work 🙂
*[_type in ['event', 'calendar', 'notice']]{
_type == 'event' => {
  eventTitle,
  eventDate,
  venue,
  mainImage,
  artists[]->,
},
_type == 'notice' => {
  _id,
  title,
  body, 
},
_type == 'calendar' => {
  _id,
  month,
  'event': event[]->{
  title[],
  date,
  },
},
}
Glad you got it working! 🙌
Ok so changing the
:
to a
=>
made it work 🙂
*[_type in ['event', 'calendar', 'notice']]{
_type == 'event' => {
  eventTitle,
  eventDate,
  venue,
  mainImage,
  artists[]->,
},
_type == 'notice' => {
  _id,
  title,
  body, 
},
_type == 'calendar' => {
  _id,
  month,
  'event': event[]->{
  title[],
  date,
  },
},
}
Thanks
user A
:)
Would you have any tips on why I am now having trouble using map() to fetch the calendar part of my eventsUpcoming query for a component now?
{eventsUpcoming.calendar.map((calendar) => (
   <IndexCal
    key={calendar._id}
    heading={calendar.month}
    event={calendar.event}
    />
))}
Would you have any tips on why I am now having trouble using map() to fetch parts of this query for a component now?
{eventsUpcoming.calendar.map((calendar) => (
   <IndexCal
    key={calendar._id}
    heading={calendar.month}
    event={calendar.event}
    />
))}
This is my getServerSideProps:
export const getServerSideProps = async () => {
const eventsUpcoming = await sanityClient.fetch(eventsUpcomingQuery);
  return {
    props: {
      eventsUpcoming,
    },
  };
};

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?