How to set keys for the return of a query in Sanity.io

7 replies
Last updated: Dec 8, 2022
Need help πŸ™
Is there a way to set the key for the return of a query.

For example, this query


  *[_type == 'navigation'] {
    navId {
      current
    },
    "id": navId.current,
    items[] {
      link[0],
      hasSubNavigation == true => {
        subLinks
      },
      hasSubNavigation,
      maxItems >= 0 => {
        'maxItems': maxItems - 1
      }
    }

  }
returns this:
AI Update

Unfortunately, GROQ doesn't support dynamic keys where you can use a field's value as the key name in the returned object. In your example, you're trying to return an object where each document is keyed by its navId.current value, but GROQ doesn't have this capability built-in.

What GROQ can do:

  • You can create static key names in projections: {"id": navId.current}
  • You can return arrays of objects: *[_type == 'navigation']{...}

What GROQ cannot do:

  • Use a field value as the object key name dynamically
  • Return results as an object keyed by document properties (like { "nav-1": {...}, "nav-2": {...} })

Workarounds:

  1. Transform on the client side (recommended): Query for an array and transform it in your application code:
*[_type == 'navigation'] {
  navId,
  "id": navId.current,
  items[]{ /* ... */ }
}

Then in JavaScript:

const result = await client.fetch(query);
const keyed = result.reduce((acc, item) => {
  acc[item.navId.current] = item;
  return acc;
}, {});
  1. Use array format: Keep your data as an array and use array methods like .find() when you need to access specific items by their navId.

  2. Custom GROQ functions: If you're on a plan that supports custom GROQ functions, you might be able to create a custom function for this transformation, though this is a more advanced approach.

The client-side transformation is the standard approach for this use case since it's simple, performant, and gives you the flexibility to structure your data however you need it in your application. This is a common pattern when working with GROQ - the query language is designed to return arrays of documents, and you handle reshaping into other data structures in your application code.

and I would like that instead of it returning
0
and
1
as keys it would return the
id
as the key, bonus points if the id can be converted to camel case
or maybe even explicitly naming returns found by
id
, something like this:

mainNavigation: [_type == 'navigation' && navId.current == 'main-navigation'][0]
footerNavigation: [_type == 'navigation' && navId.current == 'footer-navigation'][0]
all in the same query?
the best I got so far is this:

*[_type == 'navigation'] {
  navId.current == 'main-navigation' => {
    "mainNav": {
      "id": navId.current,
      items[] {
        link[0],
        hasSubNavigation == true => {
          subLinks
        },
        hasSubNavigation,
        maxItems >= 0 => {
          'maxItems': maxItems - 1
        }
      }
    }
  },
  navId.current == 'footer-left' => {
    "leftFooter": {
      "id": navId.current,
      items[] {
        link[0],
        hasSubNavigation == true => {
          subLinks
        },
        hasSubNavigation,
        maxItems >= 0 => {
          'maxItems': maxItems - 1
        }
      }
    }
  }
}
but it still returns index as key for the object instead of just
mainNav
and
leftFooter
Hmm, I think the problem here is that those numbers aren't actually in the data. They're just there to help distinguish the different items in the array. You could use a named field to set a 'key' on the objects (which it looks like you already got!).
yes, but I would like to know if there is a way to get and object instead of an array to be able to destructure each navigation. I kind of salved this issue using reduce like this:

const navigation = navigationArray?.reduce(
  (acc: any, navItem: SanityNavigation) => {
    return {...acc, ...navItem};
  },
  {},
);
but just wanted to be sure if returning and object with keys was an option using groq
I think you'll have to stick with the reduce method. A key has to be a hardcoded string value, so you wouldn't be able to pass in a value from the item in the array.
ok, all good! thanks.

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?