d||ell

GROQ - a GraphQL alternative?

December 30, 2020 · ☕️ 5 min read

Here below is a sample GraphQL query.

{
  users {
    id
    email
  }
}

The query above specifies that we want to retrieve all the users in our system and return only the id and the email.

Great! 😎

Now, here is also a query. See below:

*[] { id, email }

Or we could also write it this way,

*[] {
  id,
  email
}

Alright. Awesome. We call the above as Graph-Relational Object Queries or GROQ for short. And the two functions exactly the same but what exactly is GROQ?

Before We Dive In

I made a quick demo so we could see it in action. The video is 5 minutes only. If you want to skip the video and read through it, click here.


Oooops! Didn’t managed to finish my demo there. 😅

So for the last example, we want to get only results id, name and base stats. The query would be like this below:

*[ id == 4 ] {
  id,
  "name": name.english,
  base,
}

And here’s the result:

[
  {
    "id": 4,
    "name": "Charmander",
    "base": {
      "HP": 39,
      "Attack": 52,
      "Defense": 43,
      "Sp. Attack": 60,
      "Sp. Defense": 50,
      "Speed": 65
    }
  }
]

Getting Started

In today’s modern web, GraphQL has pretty much changed how we approach fetching and shaping data. For so long, we’ve gone accustomed to thinking in terms of endpoints. REST API had become the de-facto standard until the former came. One can’t simply ignore the benefits of using it but unfortunately, not everyone has the capacity to begin adopting it and/or start new.

NOTE: GraphQL is not a replacement for REST API.

So now, the question is - ”Is there a way that we could gain GraphQL’s declarative and expressive syntax without doing much work on our good old responses in JSON format?

Well, the answer to that is, YES!

Absolutely.

Graph-Relational Object Queries (GROQ)

GraphQL is a query language for API’s and GROQ is also a query language but for JSON. It carries on the work that has become so fashionable similar to the implementations of GraphQL which allows robust transformations, filters, projections, and many more.

In simpler terms, GROQ just like GraphQL allows us to describe exactly how we want to shape our data through filters, projections, and many more.

What’s great with GROQ is that it also takes it a step further…

Let’s go back to our example query above, here shown below:

*[] { id, email }

If you’re new, you’d probably reject what you’re seeing. When I first started using GROQ, I have this feeling that it’s not something I shouldn’t get myself into and learn. I was wrong, and I realized how it could be useful in other areas of my professional career as a developer.

* denotes everything, within the [] is our filter (though not required here but for the sake of example) and lastly, the { id, email } is our projection which only returns those fields.

It’s not that easy from the very beginning but just like any other thing we try to learn, it gets easier through practice.

Let’s Play

Here below are basic to advanced usages of GROQ with its corresponding GraphQL equivalent. The data is based on Pokemon’s API.

Don’t worry if you don’t understand it at this point. I’ll point you to the right resource below.

  1. Find all Pokemon of Grass-type together with their stats
*["Grass" in type] {
  "name": name.english,
  base,
}

Play with it here: https://groq.dev/OdzfsmdQ8fobJP8j567UdH

And the GraphQL equivalent below:

{
  pokemons(filter: {type: {in: "Grass"}}) {
    name: name.english
    base {
      HP
      Attack,
      Defense
      "Sp. Attack"
      "Sp. Defense"
      Speed
    }
  }
}
  1. Find all Pokemon of Grass-type together with their stats and order results by their speed in descending order.
*["Grass" in type] {
  "name": name.english,
  base,
} | order(base.Speed desc)

Play with it here: https://groq.dev/nyUE9QKQbTB8vVGYKOrAbk

And the GraphQL equivalent below:

{
  pokemons(sort: {fields: base__Speed, order: DESC}, filter: {type: {in: "Grass"}}) {
    name: name.english
    base {
      HP
      Attack,
      Defense
      "Sp. Attack"
      "Sp. Defense"
      Speed
    }
  }
}
  1. Find the total count of Pokemon of type Grass-type.
{
  "GrassPokemonsCount": count(*["Grass" in type])
}

Result:

{
  "GrassPokemonsCount": 97
}

Play with it here: https://groq.dev/nTm9ARCLuYmDlpLIMTXlIr

Why GraphQL alternative? (Real Life Usage)

In our experience, using GROQ with our REST APIs has allowed us to take its superpowers like filtering, sorting, and shaping our JSON response data however we want it. From an investment standpoint, sure you’ll have to learn GROQ’s syntax additionally but the benefits are just tenfolds when you get versatile with it.

For example in one of our eCommerce projects, we fetch the JSON data and used GROQ to do multi-level filtering based on customers’ selection.

Let’s say, given all shoes, get all of the color blue and the size which is greater than 12.

Pretty contrived example below:

import { parse, evaluate } from 'groq-js'

// We save our query here
let input = *[type == "shoes" && color == "blue" && size > 12] {
  id,
  name,
  description,
  price,
  images,
}

// Returns an ESTree-inspired syntax tree
let tree = parse(input)

// Here we fetch our JSON dataset
let dataset = await fetch(PRODUCTS_API_ENDPOINT);

// Evaluate a tree against a dataset
let value = await evaluate(tree, {dataset})

// Gather everything into one JavaScript object
let shoeProducts = await value.get()

GROQ-JS is a (work-in-progress) JavaScript implementation of GROQ which follows the official specification.

And that’s it! No need for GraphQL. We’ve supercharged our REST API without doing too much. 😊

To learn more about GROQ, head over to Sanity.io introduction, and refer to this cheat sheet anytime. I’ve also found some challenges you could try like this one here.

Additional Resources:


Personal blog by @dorelljames
I love you and coding!

Let me know your thoughts... 😊

What's next?

Continue reading articles using the links below...