3 simple things in GROQ to supercharge your frontends

Written by Knut Melvær

GROQ is Sanity’s graph-oriented query language. It lets you do rapid development with structured content. As soon as you create a document on the backend, you can instantly query it. No need to define a schema or write a resolver. You can use it with anything that speaks HTTP, but it also has clients in JavaScript, PHP, and .NET.

A basic query in GROQ looks like this: *[_type == "product"]. You read it like “select everything (*) and filter ([ ]) the selection down to every document whose _type is product. You can get far by just using the filter-capabilities in GROQ. It has many more features that make developing with it fast and convenient. Here’s a handpicked three to get you started.

1. Use projections to get exactly as little as you need

Early in the development stage, it can be useful to fetch complete documents with GROQ, but there comes a time where you want to optimize how much data you’re fetching. Then projections come in handy. You make projections by adding curly braces after the filter’s square braces. The anatomy of a GROQ query looks like this:

*[<filter>]{<projection>}

So if we get all the products with *[_type == "product"] we make the response slimmer by adding a projection to get only the _id and title:

*[_type == "product"]{_id, title}

Outputs:

[
  {
    "_id": "227bbb74-5656-4376-80b3-7424bddc5001",
    "title": "Happy Kitchen - Hamburger"
  },
  {
    "_id": "35f78f0b-8938-412d-aae0-80937ee4a744",
    "title": "Katjes Grün-Ohr Bärchen Gummies"
  },
  {
    "_id": "8a4337ae-4ec8-431f-8890-9e0b0192331a",
    "title": "Snører Lakris"
  }
]

There’s plenty of more things you can do in projections, some of those we’ll come back to in the next sections.

2. Count things!

You can use GROQ to count things. Let’s say you wonder how many products you have:

count(*[_type == "product"])
# outputs
> 11

So you can get all the products, but also have a count ready immediately:

{
  "products": *[_type == "product"],
  "total": count(*[_type == "product"])
}

Yep, this means that you can compose several GROQ-filters (and projections!) in one query. Remember that complexity comes with some cost, but if you use the CDN the queries will be geographically cached .

You can also do maths in GROQ. Say you want to calculate how many products you have available in your store:

{ 
  "availableProducts": count(*[_type == "product"]) - count(*[_type == "product" && soldOut == true]) 
}

Outputs:

{
  "availableProducts": 98
}

3. Save roundtrips by following references

One of the key features of structured content is the ability to make references between documents. When you create a reference, it is stored like this in the document:

{ 
  _id: "1ba26a25-7f35-4d24-804e-09cc76a0cd73",
  //... more data, 
  vendor: { 
    _type: "reference", 
    _ref: "15411164-418a-4f67-ac47-6ba060c61976" 
  }
}

If you’re used to RESTful APIs, you would be inclined to make a new query with the id on the _ref-key (*[_id == "<someOtherId>"]), but no need! You can join on a reference where it occurs by adding a -> in the projection. If you try this out, it can be useful to know about the ... operator, so that you can get the rest of the values without having to specify them:

*[_type == "product"]{
  ...,
  vendor->
}

This query give you the whole referenced vendor document, you can also use projections to get what you need, e.g:

*[_type == "product"]{
  title,
  vendor->{_id, title}
}

Outputs:

{
  "title": "Kit Kat Petit Matcha - Double Berry & Almond",
  "vendor": {
    "_id": "1e6f68d7-10bd-4b83-8880-a0ec0227b725",
    "title": "Nestlè"
  }
}

But there's more…

We have only scratched the surface of what you can do with GROQ. If you want to learn more you should head over to the documentation or join our Slack community and meet a bunch of developers who are using GROQ to do all manner of things.