
Grab your gear: The official Sanity swag store
Read Grab your gear: The official Sanity swag storeThe -> operator should work in nested array projections like this—there's no inherent limitation that prevents person->firstname from working when nested. The documentation shows examples like castMembers[].person->name working just fine. So let's troubleshoot the actual cause of your issue.
1. Field Name Mismatch
Double-check that the field is actually named firstname in your person document schema. Try querying just the dereferenced person to see what fields exist:
*[_type == "collection"][0].collection_books[0]->persons[0].person->This will show you the complete person document structure. The field might be named firstName, first_name, name.first, or something else entirely.
2. Draft vs Published Documents (Very Common Gotcha)
According to the Sanity perspectives documentation, when you have unpublished changes, Sanity creates a draft document with an ID prefixed by drafts.. If your referenced person document exists only as a draft, or if you're querying published documents that reference draft versions, the dereference might fail or return incomplete data.
Try specifying the previewDrafts perspective in your query parameters, which prioritizes drafts over published versions:
// In your client configuration
const client = createClient({
// ... other config
perspective: 'previewDrafts',
useCdn: false // Important: CDN doesn't support draft content
})Or test in Vision with the perspective dropdown set to "Preview Drafts".
3. Incorrect Field Path
Looking at your query structure:
"persons": collection_book->persons[]{
"type": type,
"firstname": person->firstname
}Make sure persons is actually an array of objects that each contain a person reference field. The structure should be something like:
{
persons: [
{
type: "author",
person: { _ref: "person-id-123", _type: "reference" }
}
]
}Verify this by checking what person alone returns (without the ->):
*[_type == "collection"][0].collection_books[0]->persons[0].personYou should see {"_ref": "...", "_type": "reference"}. If you see null or undefined, then the field path is wrong.
Test incrementally from the inside out:
// Step 1: Can you get the book?
*[_type == "collection"][0].collection_books[0]->
// Step 2: Can you get the persons array?
*[_type == "collection"][0].collection_books[0]->persons
// Step 3: Can you get a specific person reference?
*[_type == "collection"][0].collection_books[0]->persons[0].person
// Step 4: Can you dereference it?
*[_type == "collection"][0].collection_books[0]->persons[0].person->
// Step 5: Can you get the specific field?
*[_type == "collection"][0].collection_books[0]->persons[0].person->firstnameOne of these steps will reveal where the query breaks down.
Your nested dereference pattern is valid GROQ syntax. According to the reference access operator documentation, the -> operator internally executes a subquery to fetch the referenced document, and this works at any nesting level. The issue is almost certainly one of:
Work through those debugging steps and you should find the culprit!
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.
Content operations
Content backend


The only platform powering content operations
By Industry


Tecovas strengthens their customer connections
Build and Share

Grab your gear: The official Sanity swag store
Read Grab your gear: The official Sanity swag store