Issues with implementing custom features in Sanity v3, including accessing the sanity client within the prepare function and resolving errors with the getCliClient method...

29 replies
Last updated: May 31, 2024
hey sanity team,
I'm trying to customise some features in v3, specifically with the prepare function on document previews.
With the new approach of removing the part components and implementing hooks to access the sanity client, I am having issues implementing it inside the prepare function, since it is not a component itself. Normally I would find it useful to use the sanity client from within to be able to query the entire project to load data (like to follow a reference in the opposite direction), but now this seems impossible. Something like

preview: {
  select: {
    title: 'title',
    id: '_id'
  },
  prepare: async ({title, id}) => {
    // old approach, some pre-defined client from the parts system
    const {title: parentTitle } = await client.fetch('some query');

    // new approach with useClient hook won't work
    return {
      title: parentTitle + "-" + title,
    }
  }
}
Any suggestions?
Dec 9, 2022, 6:50 AM
Hey
user R
! This method should work for you! We're working on getting it added to the migration cheat sheet.
Dec 9, 2022, 4:10 PM
oh that's quite simple....will definitely help! Thank you so much
Dec 11, 2022, 6:31 AM
You're welcome!
Dec 12, 2022, 5:28 PM
Hi
user M
— I think this thread solves a problem I’m also working through with V3 … but as soon as I add the
const client = getCliClient({ apiVersion: '2021-08-21' })
my studio crashes with an
Uncaught error: Buffer is not defined
message. I’m having a hard time decoding what might be going on. Any suggestions? Thanks!
Dec 14, 2022, 6:15 PM
I'm not certain! We're investigating!
Dec 14, 2022, 8:42 PM
Thank you!
Dec 14, 2022, 8:43 PM
I'm getting the same error using with this way
Dec 20, 2022, 1:03 PM
Yes, so am I.
Dec 20, 2022, 1:40 PM
What's the context you're using it in?
Dec 20, 2022, 4:54 PM
I’m attempting to use the client inside a schema file, as follows: https://sanity-io-land.slack.com/archives/C9Z7RC3V1/p1671502713201259
Dec 20, 2022, 4:59 PM
Here's my attempt in v3:
import { getCliClient } from "sanity/cli";

const client = getCliClient({ apiVersion: process.env.SANITY_STUDIO_VERSION });

export default defineType({
  name: "novel",
  title: "Novel",
  type: "document",
  fields: [
    defineField({
      name: "author",
      title: "Author",
      type: "reference",
      to: { type: "author" }
    })
  ],
  initialValue: async () => ({
    author: await client.fetch(`
      *[_type == "author"][0]{
        "_type": "reference",
        "_ref": _id
      }
    `)
  })
})
Dec 20, 2022, 5:01 PM
Got it! That's an incorrect usage of that method. If you're inside of the schema, you need to use the client inside of the context that's passed into most functions as the second argument:
initialValue: async (props, context) =>  {
          const {getClient} = context
          const client = getClient({ apiVersion: '2022-12-14'})
          //other code. 
        }
That's not very clearly documented in the cheat sheet, so we're working on updating the docs!
Dec 20, 2022, 5:03 PM
Ah, great! Thanks! Yes, it wasn't clear to me which functions receive
context
.
Dec 20, 2022, 5:05 PM
Exactly, we need to clear that up 😅
Dec 20, 2022, 5:07 PM
It works! No more error. Thanks again!
Dec 20, 2022, 5:13 PM
In my case is a more specific usage, it's inside the schema level but for use it with the document-internationalization plugin function that create a function for the slug validation: https://github.com/sanity-io/document-internationalization/issues/91
Dec 20, 2022, 5:19 PM
I think it's a problem with the function implementation, because they can use the context inside the function. Thank you Racheal I will try to open a PR
Dec 20, 2022, 5:58 PM
Oh I have 2 situations, one is using the
hidden
property into the field, here I get the
context
undefined:

export default {
  name: 'moduleRef',
  title: 'Module Reference',
  type: 'document',
  icon: GrFlows,
  i18n: true,
  initialValue: {
    __i18n_lang: 'en',
  },
  fields: [
    {
      name: 'moduleRef',
      type: 'reference',
      title: 'Module',
      to: [{ type: 'module' }],
      options: { filter: ` __i18n_lang == 'en'` },
    },
    {
      title: 'Module ID',
      name: 'moduleId',
      type: 'slug',
      description: 'This ID is necessary if some link need to scrolls to this specific module.',
    },
    {
      title: 'Variant',
      name: 'variant',
      type: 'string',
      components: {
        input: VariantField,
      },
      localize: false,
      hidden: async ({ parent }, context) => {
        console.log(context);
        const { getClient } = context;
        const client = getClient({ apiVersion: '2022-12-14' });
        const moduleData = await sanityClient.fetch(`
          *[_id == '${parent.moduleRef?._ref}'][0]
        `);
        console.log({ moduleData });
      },
    },
  ],
And another situation is using the
isUnique
property of a slug field getting *`Uncaught error: Buffer is not defined`*:
//Schema
export default {
  name: 'page',
  title: 'Pages',
  type: 'document',
  icon: GrDomain,
  i18n: true,
  initialValue: {
    __i18n_lang: 'en',
  },
  preview: {
    select: {
      title: 'internalName',
    },
  },
  fields: [
    {
      name: 'internalName',
      type: 'string',
      title: 'Internal name',
      description: 'For internal use within Sanity',
      localize: false,
    },
    {
      name: 'slug',
      type: 'slug',
      title: 'Slug',
      description: 'Used to generate the page URL (eg., "/careers" or "/contact")',
      validation: (rule: Rule): Rule => rule.required().error('You need a unique slug for the URL'),
      options: {
        source: 'internalName',
        maxLength: 200,
        isUnique: isSlugUnique,
      },
    },

//Function
export const isSlugUnique = (_, context: ConfigContext) => {
  const { getClient } = context;
  const client = getClient({ apiVersion: '2022-12-14' });
  return createIsSlugUnique(client);
};
Dec 20, 2022, 6:31 PM
Hidden doesn't support promises, so that's likely why it's not passing in the client. You'll need to use a custom component and conditional rendering to show/hide a field based off of a value inside of a reference. It should be receiving other arguments, though. Hmmm....I'll look into how it works with isUnique!
Dec 20, 2022, 7:43 PM
Ok, the client is located like so in your `isUnique`:
isUnique: async (value, {client}) => {}
Dec 20, 2022, 7:57 PM
Thank you Racheal, using a custom component as an input I don't know if I have control of the field label, for hidden it entirely. Do you know if we can do that? Maybe makes sense I create a new thread for that...
Dec 21, 2022, 12:01 PM
Related to the
isUnique
I'm getting the same error (https://github.com/sanity-io/document-internationalization/issues/91 ) using both (
client
and
getClient
) from the context
Dec 21, 2022, 12:05 PM
user U
you can use Field components to completely obscure the field, including the title. More on the difference between the two here .
Here's how you'd use the client in your isUnique function:

const isSlugUnique = async (value, {client}) => {
  const slugs = await client.fetch(`*[defined(slug)].slug.current`)

  return !slugs.includes(value)
}

//other code 

  {
      name: 'slug',
      title: 'Slug',
      type: 'slug',
      options: {
        source: 'title',
        maxLength: 96,
        isUnique: isSlugUnique,
      },
    },
Dec 21, 2022, 4:40 PM
Awesome! Thank you Racheal you are great!
Dec 21, 2022, 7:31 PM
Aw thank you!
Dec 21, 2022, 7:32 PM
user M
Please can you update the migration cheat sheet for this. There’s still a bunch of old references to v2 , but nothing about how to do this for v3.
Jan 11, 2023, 12:30 PM
Yes, the migration docs needs to be updated.
Jan 28, 2023, 7:57 PM
Where do I put my custom function here, I keep getting a promisify error. Can I run the async function
import { getCliClient } from 'sanity/cli'
const client = getCliClient({ apiVersion: '2021-10-21' })

async function asyncCampaignCodeGenerator(input, context) {
  var chars = 'acdefhiklmnoqrstuvwxyz0123456789'.split('');
  var codeGen = '';
  for (var i = 0; i < 8; i++) {
    var x = Math.floor(Math.random() * chars.length);
    codeGen += chars[x];
  }
  const query = `*[_id == "${input}"]{ "countryCode": jurisdiction->code }`
  const params = { code: input }
  return context.client.fetch(query, params).then(code =>  `${ code[0]?.countryCode || 'G' + '-' + codeGen.toUpperCase()}`)
}
inside the schema file or not?
I am calling it from within a definedField like this:

defineField({
      name: 'code',
      type: 'slug',
      title: 'Campaign Code',
      description: 'This will be the unique campaign code',
      options: {
        source: '_id',
        slugify: asyncCampaignCodeGenerator
      },
      validation: Rule => Rule.required()
    })

Feb 28, 2023, 6:57 PM
is there a fix for this?
May 31, 2024, 9:13 PM

Sanity– build remarkable experiences at scale

Sanity is a modern headless CMS that treats content as data to power your digital business. Free to get started, and pay-as-you-go on all plans.

Was this answer helpful?