GROQ: Making query parameters optional for conditional filtering

18 replies
Last updated: Jan 19, 2022
Sanityland! Is there a way to make GROQ params optional?
ie. I want to conditionally provide a filtering param to a GROQ query. If no param is provided, don't filter. If provided, filter.

This is my current solution:

*[_type == 'feed' ${id ? ' && _id == $id' : ''}]
Is there a way to do this without JS template literals?
AI Update

The approach you're using with template literals works, but there is a better way using GROQ's built-in select() function for conditional logic!

Here's how to handle optional parameters directly in GROQ:

*[
  _type == 'feed' 
  && select(
    defined($id) => _id == $id,
    true
  )
]

The select() function evaluates conditions in order and returns the result of the first matching condition. When $id is defined, it applies the filter _id == $id. When it's not defined, it returns true, which effectively means "no filter applied."

Important caveat: In GROQ, parameters cannot be undefined - they must either be present with a value or explicitly passed as null. So you'd call it like:

// With an ID
await client.fetch(query, { id: 'some-id' })

// Without an ID
await client.fetch(query, { id: null })

Alternative approach (arguably cleaner for this specific case): Build the query conditionally in your JavaScript, but still use parameters for the actual values:

const query = id 
  ? `*[_type == 'feed' && _id == $id]` 
  : `*[_type == 'feed']`
const params = id ? { id } : {}

const data = await client.fetch(query, params)

This approach from the Sanity documentation keeps your GROQ cleaner while still using parameters for safety. The key advantage of using $id instead of template literal interpolation is security and proper escaping - parameters are JSON literals that can't contain GROQ expressions, preventing injection attacks.

Show original thread
18 replies
Unfortunately, you can't apply something like this within your query. What's the context that you're using this in? Can you apply some logic to your params before you provide them?
user M
The context is just that I'd like to be able to have a single query. If I provide a document ID as a param, the query would use the provided ID to return that document. If I do not provide a document ID (aka, no params at all) that query would just return all documents of that type.
Right now, I would need to write 2 separate queries, one that expects a param of
$id
, the other that does not.
I mean, where are you performing this query? Is it in the Studio? On a frontend?
Oh, performing the query on an API endpoint in Nextjs, so... frontend... ish?
Got it. So you should be able to perform some JS on the params before you pass them to your client.
const query = id ? `*[_type == 'feed' && _id == $id]` : `*[_type == 'feed']`
const params = id ? { id } : {}

const data = sanityClient.fetch(query, params)

Yeah, that's our approach currently. I suppose there's nothing that can be done within GROQ with params eh? If params are defined in the query, they must be provided, eh?
There's no GROQ conditional to support optional params?
Unfortunately, no conditional params at this time.
Okay, thanks! 🙏
Happy to help!
I think for this specific use case, you can use
coalesce()
to filter by
id
only if
id
is not
null
:

*[_type == 'feed' && id == coalesce($id, id)]
If no
$id
is present, it’ll fall back to
id == id
which always evaluate to
true
.
See an example here:
https://groq.dev/1zBjak9JVZ2ct7eURppBJN
When the parameter
{"userId":2}
is present, it’ll only query todos from that users; otherwise it’ll query everything
I think for this specific use case, you can use
coalesce()
to filter by
id
only if
id
is not
null
:

*[_type == 'feed' && id == coalesce($id, id)]
If no
$id
is present, it’ll fall back to
id == id
which always evaluate to
true
.
See an example here:
https://groq.dev/1zBjak9JVZ2ct7eURppBJN
When the parameter
{"userId":2}
is present, it’ll only query todos from that users; otherwise it’ll query everything
it’s not exactly the same as your string template literal query since
coalesce()
does not falsify value like JS (e.g if id is an empty string it’ll return nothing), so you’d still need some JS magic but at least the query is a little more readable
Thanks
user G
. I actually had tried that in Vision in the Studio and it returns an error stating:
Query error
param $id referenced, but not provided
I'll try it out in code and see if that was a unique case to Vision or not. Thanks for the tip!
ohh I guess groq.js is a little more relaxed with missing params. I wonder if it’d work if you pass in null as the param? If it does, on javascript side, for your function, you can give
null
as the default value for the
_id
ohh I guess groq.js is a little more relaxed with missing params. I wonder if it’d work if you pass in null as the param? If it does, on javascript side, for your function, you can give
null
as the default value for the
_id
I know this is a little bit old but I ran into the same problem and this is what I came up with:
// The simplified query
*[_type == "blogPost" ${categoryId ? optionalCategoryFilter : ''}]
  ${keyword ? optionalKeywordFilter : ''}
  | order(${keyword ? '_score desc,' : ''}_createdAt desc)

optionalCategoryFilter
and
optionalKeywordFilter
is query fragments as plain string.The whole query is a monstrosity but at least it beats having to write multiple different queries to account for the absence of filter parameter.

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?