✨Discover storytelling in the AI age with Pixar's Matthew Luhn at Sanity Connect, May 8th—register now

Comparison of GROQ and GraphQL query languages for data manipulation and API definition.

8 replies
Last updated: Apr 8, 2022
is there anything you miss out on by going with GROQ vs GraphQL and vice versa?
Apr 5, 2022, 2:57 PM
We ended up using groq since (at least at the time), doing almost anything with the portable text was impossible in graphql, like expanding references and such. This might have changed since we last looked at it last year
Apr 6, 2022, 5:54 AM
To go a bit deeper, and to steal some quotes and notes from others here at Sanity, one of my colleagues User likes to describe it this way:“You can think of GraphQL as replacing RESTful and GROQ as ‘replacing’ SQL (but for JSON)”
“You can make a GraphQL API with GROQ, but you can’t make a GROQ API with GraphQL.”
“GROQ is excellent for early data/content massaging when your patterns aren’t fully found yet. GraphQL is great if you want to use its ecosystem, or you want the strictness and types.”

And User, one of the folks who works a lot on GROQ, shared this:

The big difference between the two is that GROQ is a query language, and GraphQL is a language for defining APIs. GROQ can query arbitrary data structures and lets you build pipelines that change the shape of the data and add computations, and all of it happens within the query engine.

With GraphQL, you have to define a schema, and all interactions are locked to this schema. It is not possible to express a GraphQL request that changes the shape of the output. Any mechanism for ad-hoc querying (e.g. pagination or filtering) must be custom-built by the server implementor (directives).

Given a simple schema such as:

type Foo {
  bars: [Bar]
  baz: String
In GraphQL you can now do things like:

// get just bars
foo { bars } 
// get bars and baz
foo { bars, baz } 
…and that’s about it, if we ignore directives and fragments. But in GROQ you can now do all sorts of stuff:

// get bars as a property of foo
foo { bars }
// get just bars
// get just the items in bars[] where the color property == "brown"
foo.bars[color == "brown] 
// get just the first three elements of bars
// get a count of the number of bars with color == brown
count(foo.bars[color == "brown])
// get everything in foo, plus the count of items in bars[]
foo { ..., "barCount": count(bars) } 
// get the number of bars where color == brown, as a property of foo
foo { "brownBarCount": count(bars[color == "brown"]) }
// get a list of just the names of the bars where color is brown.
{"brownBarNames": bars[color == "brown"].name}
etc. etc.
These kinds of ad-hoc querying capabilities shift all the power and flexibility to the front end. You can build new apps and clients without going back and modifying the server.
Apr 7, 2022, 10:42 PM
Hopefully this helps
Apr 7, 2022, 10:42 PM
i think graphql could actually do all of that
Apr 7, 2022, 10:59 PM
resolver would take query arguments (just like how the top-level foo does)
allFoo(where: {someFooField: {eq: "thing1"}}) {
  bars(where: {someBarsField: {eq: "thing2"}}) {
    bar {

the bars resolver knows which fields are requested, so only has to calculate _count on demand (and could itself take params)
and it knows the parent foo object
Apr 7, 2022, 11:02 PM
only saying that this is possible in graphql, not that i see it in the sanity docs
Apr 7, 2022, 11:04 PM
one big benefit we get from (apollo) graphql is the caching layer. If one query sees an update to an object, it is updated everywhere -- even for queries that didn't rerun.
Apr 7, 2022, 11:12 PM
yeah, I meant the graphql-api that sanity provides. If I remember correctly, at the time at least, all portable-text fields were just
types, so you couldn’t do anything with them in the query except return them
Apr 8, 2022, 6:36 AM

Sanity– build remarkable experiences at scale

Sanity is a modern headless CMS that treats content as data to power your digital business. Free to get started, and pay-as-you-go on all plans.

Was this answer helpful?