GROQ vs GraphQL: What are the tradeoffs?

8 replies
Last updated: Apr 8, 2022
is there anything you miss out on by going with GROQ vs GraphQL and vice versa?
AI Update

Both GROQ and GraphQL are fully supported by Sanity for querying content from the Content Lake, but each has distinct strengths and limitations. Here's what you should know:

What GROQ offers that GraphQL doesn't:

Real-time subscriptions: GROQ works with Sanity's listening capabilities and real-time features, while GraphQL subscriptions aren't currently supported. If you need live content updates, you'll want to use GROQ with the JavaScript client.

Native query language advantages: Since GROQ is Sanity's native query language, it's designed specifically for querying structured content with powerful projections and transformations. It tends to be more flexible for complex content manipulation and can feel more intuitive for Sanity-specific operations.

No deployment step: GROQ works out of the box without needing to deploy an API schema.

What GraphQL offers that GROQ doesn't:

Schema introspection and autocomplete: GraphQL provides automatic documentation and autocomplete in GraphQL playgrounds, making it easier to explore your content model.

Type generation: With tools like GraphQL Code Generator, you can automatically generate TypeScript types from your GraphQL schemas, giving you strong type safety throughout your application.

Ecosystem familiarity: If your team already uses GraphQL or you're working with frameworks like Gatsby (which uses gatsby-source-sanity with GraphQL), GraphQL provides a familiar interface with existing tooling.

Single request efficiency: GraphQL's ability to request exactly what you need in one query can be more intuitive for developers coming from REST backgrounds.

The good news:

You're not locked into one or the other! You can use both in the same project. For example, some developers use GraphQL with Apollo Client for data fetching while using GROQ for live preview subscriptions. Both query languages access the same Content Lake and support features like Perspectives for draft/published content views.

The choice often comes down to team familiarity and specific requirements. If you need real-time updates, GROQ is currently your only option. If you want strong typing and schema introspection, GraphQL might be preferable.

Show original thread
8 replies
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
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
foo.bars 
// get just the items in bars[] where the color property == "brown"
foo.bars[color == "brown] 
// get just the first three elements of bars
foo.bars[0..2] 
// 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.
Hopefully this helps
i think graphql could actually do all of that
`Foo`'s
bars
resolver would take query arguments (just like how the top-level foo does)
allFoo(where: {someFooField: {eq: "thing1"}}) {
  bars(where: {someBarsField: {eq: "thing2"}}) {
    bar {
      Field1
    }
    _count
  }
}
etc

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
only saying that this is possible in graphql, not that i see it in the sanity docs
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.
yeah, I meant the graphql-api that sanity provides. If I remember correctly, at the time at least, all portable-text fields were just
string/text
types, so you couldn’t do anything with them in the query except return them

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?