GROQ introduction
GROQ (Graph-Relational Object Queries) is Sanity's powerful query language designed to help you describe exactly what information your application needs.
With GROQ, you can join information from multiple documents, filter with precision, and stitch together specific responses containing only the exact fields you need.
GROQ lets you control the shape of your results, so you can transform content at the API level rather than in your application code.
With GROQ, you can:
- Extract precisely what you need from your documents, reducing bandwidth usage and simplifying your frontend code.
- Join related content by following references between documents, creating rich, nested responses.
- Filter and sort your content with powerful expressions and functions.
- Transform your data at the query level with projections and computed fields.
- Create modular, reusable queries with custom GROQ functions.
Core concepts
GROQ operates as a pipeline where data flows from left to right through various operations. Understanding these core concepts helps you build effective queries.
Where can you use GROQ?
While GROQ is primarily thought of as a means of querying data for your frontends, GROQ queries are available in most places where you interact with data in a Sanity dataset. You can send GROQ queries with the fetch() method in @sanity/client, directly to the /data/query HTTP endpoint, and more.
In some use cases, you'll pass an entire query as part of your request. In others, such as in Functions or Webhooks, you configure the filter and projection separately.
Syntax
The following are the most common parts of the GROQ syntax. For a full list, explore the GROQ syntax reference.
Filters
Filters let you select specific documents from your dataset based on criteria. Most GROQ queries start with * (representing all documents) followed by a filter in square brackets.
*[_type == "movie" && releaseYear >= 1979]
This query selects all movie documents released in or after 1979.
Projections
Projections define the shape of your results, letting you pick exactly which fields to include. They're enclosed in curly braces.
*[_type == "movie"]{ _id, title, releaseYear }This returns only the ID, title, and release year for each movie, even if the documents contain many more fields.
References and joins
With GROQ, you can follow references between documents using the dereferencing operator (->). This lets you include related content directly in your results.
*[_type == "movie"]{
_id,
title,
"director": director->name
}This query follows the reference in the director field and includes only the director's name in the results.
Sorting and slicing
You can order your results and select specific portions of the result set.
*[_type == "movie"] | order(releaseYear desc) [0...10]
This sorts movies by release year (newest first) and returns only the first 10 results.
Functions
GROQ functions provide powerful tools for manipulating and retrieving data. GROQ includes built-in functions like count(), order(), and references() that help you analyze, sort, and manipulate your content. You can see all available functions in the GROQ functions reference documentation.
You can also create your own custom GROQ functions to make your queries more modular and reusable.
Perspectives
Perspectives control which version of a document your GROQ query returns. Every document in the Content Lake can exist in multiple states (published, draft, or as part of a content release), and the perspective determines which state you see.
Setting a perspective
Set the perspective in your client configuration or on individual queries. Drafts and release versions are only returned to authenticated requests, so query the drafts perspective from the server with a read token:
import {createClient} from '@sanity/client'
const client = createClient({
projectId: 'YOUR_PROJECT_ID',
dataset: 'YOUR_DATASET',
apiVersion: '2026-09-03',
perspective: 'published',
useCdn: true,
})
// Override per query. Drafts are only returned to authenticated
// requests, so run this on the server with a read token.
const drafts = await client.fetch(
'*[_type == "article"]',
{},
{
perspective: 'drafts',
useCdn: false,
token: process.env.SANITY_API_READ_TOKEN,
},
)Or pass it as a query parameter in the HTTP API:
curl -G "https://YOUR_PROJECT_ID.api.sanity.io/v2026-09-03/data/query/YOUR_DATASET" \ --data-urlencode 'query=*[_type == "article"]' \ --data-urlencode 'perspective=drafts' \ -H "Authorization: Bearer $SANITY_API_READ_TOKEN"
For a complete guide to perspectives, including how they interact with content releases and preview workflows, see Perspectives for Content Lake.
The Vision plugin
The Vision plugin adds a playground for testing GROQ queries directly to Sanity Studio. You can use it to try out queries, analyze your data, and experiment.
The GROQ specification
GROQ has a formal specification that defines its syntax and behavior. If you're building your own tools on top of GROQ, the specification is available at spec.groq.dev and serves as the authoritative reference for how GROQ should work.
Limitations
- Custom GROQ functions have some limitations. Function bodies must use one of four supported projection formats, and functions don't support recursion, accessing the parent scope, more than one parameter, or accessing the parameter more than once in the function body. For more details, see the Custom GROQ functions guide.
- Complex queries with many joins or large result sets may impact performance. Learn more about how to write performant GROQ queries.