GROQ query injection security and parameter sanitization

5 replies
Last updated: Jan 28, 2026
Is there a place I can read about GROQ queries and security?I stumbled upon queries like
*[_type == "page" && slug.current in ${slugs}]
and given that in that case slugs come from the URL I assume there's an GROQ injection risk there?Can't find much material on the topic.
AI Update

You're absolutely right to be concerned about GROQ injection! The good news is that Sanity has built-in protection through parameterized queries, which is exactly what you should be using for cases like this.

The Problem with String Interpolation

When you write something like:

const query = `*[_type == "page" && slug.current in ${slugs}]`

You're doing JavaScript string interpolation, which means you're directly inserting user input into your query string. This is indeed vulnerable to GROQ injection attacks and can also cause syntax errors if the values contain special characters like quotes.

The Solution: Use GROQ Parameters

Instead, use GROQ parameters with the $ syntax:

const query = `*[_type == "page" && slug.current in $slugs]`
const params = { slugs: slugsFromUrl }

const results = await sanityClient.fetch(query, params)

Parameters are passed as the second argument to client.fetch(), and the Sanity client automatically handles proper encoding and escaping.

Why Parameters Are Secure

GROQ parameters provide several critical security benefits:

  1. Prevention of GROQ injection: Parameters must be JSON literals, so they can only contain values (not arbitrary GROQ expressions). This means user input can't alter the structure of your query.

  2. Automatic escaping: The client library properly escapes special characters, preventing syntax errors and injection attempts.

  3. Type safety: Parameters maintain their data types throughout the query execution.

As the Sanity documentation explains, since parameters are JSON literals, they cannot contain GROQ expressions that could alter the query structure, making them safe to pass from user input.

Best Practices

  • Always use $parameters for dynamic values, especially anything coming from user input (URLs, form fields, etc.)
  • Use the Sanity JavaScript client when possible—it handles parameter encoding automatically
  • If a parameter might be missing, pass null or use conditional logic with select()

The GROQ parameters specification has more details on the technical implementation if you want to dive deeper into how this works under the hood.

Show original thread
5 replies

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?