Is it Possible to Pass a Document Reference to Validation and Access the Dereferenced Fields?

2 replies
Last updated: Aug 6, 2021
Heyyy I have a schema validation question. Is it possible to pass a doc reference to validation and access the dereferenced fields? It seems not. It also seems like the solution is to perform a fetch to get it. Is there anything I'm missing here?
AI Update

You're absolutely right – reference fields in Sanity validation don't automatically dereference, and using Rule.custom() with a fetch is indeed the standard solution for this.

When you access a reference field in validation, you only get the reference object itself (with _ref, _type, etc.), not the actual referenced document's fields. To access those fields, you need to fetch the referenced document using the Sanity client.

Here's the pattern using Rule.custom() with the validation context:

defineField({
  name: 'authorRef',
  type: 'reference',
  to: [{type: 'author'}],
  validation: (rule) =>
    rule.custom(async (value, context) => {
      if (!value?._ref) return true;
      
      const client = context.getClient({apiVersion: '2024-01-01'});
      const author = await client.fetch(
        `*[_id == $id][0]{name, isActive}`,
        {id: value._ref}
      );
      
      if (!author?.isActive) {
        return 'Please select an active author';
      }
      return true;
    }),
})

The key things to know:

  1. Use context.getClient() to get a configured Sanity client within your validation function
  2. Make the validation function async since you're fetching data
  3. Access the reference ID via value._ref to query for the document
  4. Return true for valid or an error string for invalid

You can also access other document fields through context.document or the parent value through context.parent, which is useful for conditional validation based on other fields in your document.

One thing to watch out for: these validation functions run client-side in the Studio, so fetching on every keystroke could impact performance. Consider debouncing or only validating on blur if you're doing expensive queries.

There's no built-in "auto-dereference" option for validation – the fetch approach is the intended pattern for accessing referenced document data during validation.

I expect you’re right that a fetch will be involved. I would use the Sanity client and get the data you’re after in that fetch (in my case below, the title). This snippet lets you paste a document _id into a string field and if it exists, it throws a validation error that contains that document’s title. I’d recommend looking at this function to add some robustness to the query:

import sanityClient from 'part:@sanity/base/client';

const client = sanityClient.withConfig({ apiVersion: '2021-08-06' });

// ...

export default {
  name: 'validationTest',
  title: 'Validation Test',
  type: 'document',
  fields: [
    {
      name: 'id',
      title: 'ID',
      type: 'string',
      validation: Rule => Rule.custom(async (doc) => {
        const match = await client.fetch(`*[_id == $id][0] {_id, title}`, { id: doc })
        <http://console.info|console.info>(match)
        return !match ? true : `This document already exists. Title: ${match.title}`
      })
    }
  ]
}
thanks, Geoff!

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?