GROQ v1 auto-dereferences arrays, v2+ returns refs - how to dereference?
*[_id == '90ce29b2-cab4-4790-bd97-57e1a97adaf5'][0] {
'blocks': blocks[]->,
...}everything else as null:
// v1
result: {
//...
"blocks": [
null, // <----------------- missing ref
{
"_createdAt": "2022-06-15T17:25:28Z",
"_id": "f66bfdca-f03a-4bfe-a4fa-cda7537a448a",
"_rev": "kwp71lZJIGHA037Eneiyj2",
"_type": "prose",
"_updatedAt": "2022-06-29T16:37:37Z",
"content": [ /*...*/]
}
]
//...
}them as references instead.
// v2021-03-25
result: {
//...
"blocks": [
{
"_key": "293cc22154b1",
"_ref": "968147d3-a988-4b05-8faa-21a1d3182650",
"_type": "linkMenuRef",
"_weak": true
},
{
"_key": "f280c4e697e1",
"_ref": "f66bfdca-f03a-4bfe-a4fa-cda7537a448a",
"_type": "proseRef",
"_weak": true
}
],
//...
}I understand you're experiencing a change in how blocks[]-> behaves between GROQ v1 and newer API versions like v2021-03-25. This is indeed a documented change related to API versioning.
The issue you're seeing is real: in v1, blocks[]-> would dereference references and return null for missing ones, but in v2021-03-25 and later, the dereferencing behavior changed and the syntax returns the reference objects themselves when placed after other projections.
Based on a community discussion around this exact issue, there's a documented workaround:
The solution is to place the spread operator (...) BEFORE your blocks[]-> projection:
*[_id == '90ce29b2-cab4-4790-bd97-57e1a97adaf5'][0] {
...,
'blocks': blocks[]->
}This ordering matters in v2021-03-25 and later versions. When you put the spread operator first, the dereferencing works as expected, returning dereferenced objects with null for missing references.
This does NOT work:
*[_id == '90ce29b2-cab4-4790-bd97-57e1a97adaf5'][0] {
'blocks': blocks[]->,
...
}The order of operations in the projection affects how the dereferencing is processed in newer API versions. This appears to be a parsing quirk in how GROQ handles projections when combining dereferencing with the spread operator.
Alternative approach if you want to filter out nulls entirely:
If you want to exclude missing references rather than having null values in your array, you can filter them after dereferencing:
*[_id == '90ce29b2-cab4-4790-bd97-57e1a97adaf5'][0] {
...,
'blocks': array::compact(blocks[]->)
}Or use a filter to only include defined references:
*[_id == '90ce29b2-cab4-4790-bd97-57e1a97adaf5'][0] {
...,
'blocks': blocks[defined(@->)]->
}This is a known behavior difference between v1 and v2021-03-25+ related to how the GROQ parser handles projection ordering when dereferencing is involved. The workaround of placing ... first in your projection should restore the v1-like behavior you're looking for.
Show original thread7 replies
Was this answer helpful?
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.