How to dereference nested references in GROQ projections?

9 replies
Last updated: Mar 14, 2023
Can someone help me format this query? I have a document (type A) that has two arrays, which each reference other documents (type B & C, respectively). I initially queried for documents of type A && with the right name, then made two naked projections for each array of documents, like this:`'typeB': typeBDocuments[].typeBDocument->` so that they dereferenced (I think that is the right terminology?).
The problem is that these documents each have references inside them, and so I either need to learn how to do multi-layer dereferencing, or I need to query documents of type B & C which are referenced by the type A document.

I’m happy to give access to the repository if you want to see the thing.
AI Update

Based on your description, you're dealing with nested references in GROQ, and you're on the right track! The -> operator does indeed dereference references, and you can chain multiple dereferences to follow nested references as deep as you need.

For multi-layer dereferencing, you can simply chain the -> operators. Here's how it works:

*[_type == "typeA" && name == "yourName"]{
  'typeB': typeBDocuments[].typeBDocument->{
    // Fields from type B document
    title,
    someField,
    // Now dereference nested references inside type B
    'nestedRef': nestedReference->
  },
  'typeC': typeCDocuments[].typeCDocument->{
    // Fields from type C document
    name,
    // Dereference nested references inside type C
    'nestedRef': anotherReference->{
      // You can even go deeper
      field1,
      field2,
      'deeperRef': yetAnotherReference->
    }
  }
}

The key points:

  1. Chaining works: You can use -> multiple times to follow references as deep as needed
  2. Array handling: When your reference is inside an array, the syntax array[]-> automatically maps over the array and dereferences each item
  3. Projection control: Inside each {}, explicitly list which fields you want from the dereferenced document

If you want to be more selective about which fields come back at each level, you can be explicit:

*[_type == "typeA" && name == "yourName"]{
  name,
  'typeBData': typeBDocuments[].typeBDocument->{
    _id,
    title,
    description,
    'relatedItem': relatedItemRef->{
      _id,
      name,
      imageUrl
    }
  }
}

This approach is covered in the projections documentation, which explains how projections let you follow references and build custom data structures.

The alternative approach you mentioned—querying type B & C documents separately—would be less efficient since you'd need multiple queries and then manually match them up in your application code. The nested dereferencing approach keeps everything in a single query and returns the exact shape you need.

If you're still having trouble with the syntax, feel free to share your actual query and schema structure, and the community can help you format it more specifically!

Show original thread
9 replies
You can dereference as deep down the rabbit hole as you want, for eg,this is a 3 layer deep dereference.

*[_type == 'A']{
    B[]->{
        C->{ someProperty }
    }
}
Of course, you could also query by document type B and C to get the data you need - it all depends on how you intend to use the information in the end.
user T
thank you very much! I was missing the second set of curly brackets
Actually, I spoke too soon. Here is the actual GROQ
*[_type == 'collection' && title == "${collectionName}"] {'products': products[].product->, 'sets': sets[].set->}
which gives me the following data (image attached). As you can see there, the sets’
image.asset
is a reference, which I need to dereference.
Any chance you can help me with this more specific question? THANKS!
It's not advisable to dereference an image asset for presentation via groq. I would recommend using the Image Builder library to dynamically construct the url you need to render the image on the frontend.
great, I’ll look into that. Thanks!
I’ve installed the image-url builder , but I’m confused on how to use the next set of instructions here . I don’t know how to “pass it your configured sanityClient”. Sorry for the n00b question…
Passing a "configured Sanity client" is a two step process of
• first, importing the relevant packages into a 're-usable' file and adjusting the values of the properties to reflect your own studio/project, similar o the example in your second linked, and then
• second, referencing that exported setup from that file into the file where you'll use it
Having the "client" in one place saves you from having to copy and paste the project details every time you want to connect to your data in the code.

Imagine that I had to introduce myself to shake hands with everyone I met. I'd have a file like this

// nameDetails.js

export const name = {
   first: 'Vincent',
   last: 'Florio'
}
and then everywhere I wanted to shake hands, I could just say

import { myName } from nameDetails.js

const handshake = shakeHandsUsing( myName )
Thanks for the explanation! So does this require a build process or active server architecture (to run node)? What would the reusable file be named?
You are able to select whichever name you please for the file, just as long as it matches when you import it into the file where it's being used in just the same way.
I don't know that it requires a server architecture because the requests are all able to be made client-side; that said, I think even in regular React (not necessarily Next.js for example) you still have to export to a static build to make sure it all operates correctly, but someone else might be able to weigh in on that.

There are some solid
"starter" projects you can fire up with a free Vercel integration if you're interested in experimenting.

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?