Filename validation for file type field in Sanity schema

4 replies
Last updated: Oct 6, 2021
Hello everyone! Has anyone ever done any filename validation in a schema with the "file" data type? I'll be periodically uploading newer versions of files and need to make sure I'm not adding the wrong file in any document.

export default {
    name: 'download',
    title: 'Download',
    type: 'document',
    fields: [
        {
            name: 'name',
            title: 'Name',
            type: 'string',
            validation: (Rule) => Rule.required(),
        },
        {
            name: 'description',
            title: 'Description',
            type: 'string',
        },
        {
            name: 'filename',
            title: 'Filename',
            description: 'This will be the filename which the uploaded file must have.',
            type: 'string',
            validation: (Rule) => Rule.required(),
        },
        {
            name: 'file',
            title: 'File',
            type: 'file',
            validation: (Rule) => Rule.required(),
        },
        {
            name: 'active',
            title: 'Active',
            description: 'Is this download active?',
            type: 'boolean',
            initialValue: true,
        },
    ],
}
If I try to do document level validation, I don't know how to get the original file name. Doing the following only gets me the hashed filename.

validation: (Rule) =>
    Rule.custom((doc) => {
        const matched = doc.filename === doc.file.asset._ref;
        return matched
            ? 'Uploaded filename does not match the expected filename.'
            : true
        return true;
    }),
AI Update

Good question! Yes, you can validate the filename by accessing the asset document's originalFilename property. The key issue you've identified is correct - doc.file.asset._ref only gives you the asset reference ID, not the actual filename.

Here's how to validate the filename using custom validation with the Sanity client:

Solution: Custom Validation with Asset Lookup

You can use custom validation that fetches the asset document to access the originalFilename:

export default {
    name: 'download',
    title: 'Download',
    type: 'document',
    fields: [
        {
            name: 'name',
            title: 'Name',
            type: 'string',
            validation: (Rule) => Rule.required(),
        },
        {
            name: 'filename',
            title: 'Filename',
            description: 'This will be the filename which the uploaded file must have.',
            type: 'string',
            validation: (Rule) => Rule.required(),
        },
        {
            name: 'file',
            title: 'File',
            type: 'file',
            validation: (Rule) => Rule.required().custom(async (value, context) => {
                if (!value?.asset?._ref) return true;
                
                const {getClient} = context;
                const client = getClient({apiVersion: '2024-01-01'});
                
                // Fetch the asset document to get originalFilename
                const asset = await client.fetch(
                    `*[_id == $assetId][0]{originalFilename}`,
                    {assetId: value.asset._ref}
                );
                
                // Get the expected filename from the parent document
                const expectedFilename = context.document?.filename;
                
                if (!expectedFilename) return true;
                
                if (asset?.originalFilename !== expectedFilename) {
                    return `Uploaded filename "${asset?.originalFilename}" does not match expected filename "${expectedFilename}"`;
                }
                
                return true;
            }),
        },
        {
            name: 'active',
            title: 'Active',
            description: 'Is this download active?',
            type: 'boolean',
            initialValue: true,
        },
    ],
}

How it works:

  1. The context.getClient() gives you access to a Sanity client within validation rules
  2. You query the asset document using its _id (which is the _ref value)
  3. The asset document has an originalFilename field that stores the actual uploaded filename (this is stored by default as mentioned in the file type documentation)
  4. You compare it against your expected filename field from context.document

Important notes:

  • This validation is asynchronous and runs when you try to publish/save the document
  • Make sure to use an appropriate apiVersion for your project
  • The originalFilename is only stored if the storeOriginalFilename option is true (which is the default according to the file type docs)
  • The validation has access to the entire document via context.document, so you can reference other fields

This approach ensures you're validating against the actual uploaded filename, preventing mismatches when periodically updating files!

Show original thread
4 replies
Hi User! Sorry for the delay responding.
Thankfully, validation can be async, so you can use a client to dereference the file asset and get the filename:


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

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

export default {
  name: 'download',
  title: 'Download',
  type: 'document',
  fields: [
    {
      name: 'name',
      title: 'Name',
      type: 'string',
      validation: (Rule) => Rule.required(),
    },
    {
      name: 'description',
      title: 'Description',
      type: 'string',
    },
    {
      name: 'filename',
      title: 'Filename',
      description: 'This will be the filename which the uploaded file must have.',
      type: 'string',
      validation: (Rule) => Rule.required(),
    },
    {
      name: 'file',
      title: 'File',
      type: 'file',
      validation: Rule => Rule.custom(async (doc, context) => {
        const { _ref } = doc.asset
        const { filename } = context.document
        const originalFilename = await client.fetch(`*[_id == $ref][0].originalFilename`, { ref: _ref })

        return filename === originalFilename ? true : `Uploaded file ${originalFilename} does not match required filename ${filename}`
      }),
    },
    {
      name: 'active',
      title: 'Active',
      description: 'Is this download active?',
      type: 'boolean',
      initialValue: true,
    },
  ],
}
This is using the client to get the original filename of the fileAsset, then it compares it against the filename field (using the
second parameter of
Rule.custom()
). If they match, return
true
. If they don’t, tell the user why.
Thank you so much for this. I would have never thought of doing it that way.
Thank you so much for this. I would have never thought of doing it that way.
No problem, User! Glad that works for you.

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?