-
-
Save employee451/2f05d49b5d8251e2acb25fe408426b83 to your computer and use it in GitHub Desktop.
import { groq } from 'next-sanity' // Replace with another library if you don't use Next.JS | |
import { client } from './client' // Replace this with wherever you have set up your client | |
/** | |
* Resolves all unresolved references when fetching from the Sanity GraphQL API. | |
* There is no native way to do this, so we have to do it manually. | |
* @param data | |
*/ | |
export const resolveSanityGraphqlReferences = async <T = unknown>( | |
data: T | |
): Promise<T> => { | |
// If we are not dealing with an object, simply return the original value | |
if (typeof data !== 'object' || !data) { | |
return data | |
} | |
return Object.entries(data).reduce<any>(async (acc, [key, value]) => { | |
// If it's a reference, we need to resolve it | |
if (value?._type && value._type === 'reference') { | |
const resolvedReference = await client.fetch( | |
groq`*[_id == $ref][0]`, | |
{ | |
ref: value._ref, | |
} | |
) | |
return { | |
...(await acc), | |
[key]: resolvedReference, | |
} | |
} | |
// If it's a nested object (not a reference), we continue the crawling | |
if (value?._type) { | |
return { | |
...(await acc), | |
[key]: await resolveSanityGraphqlReferences(value), | |
} | |
} | |
// For arrays, crawl every item in the array | |
if (Array.isArray(value)) { | |
return { | |
...(await acc), | |
[key]: await Promise.all(value.map(resolveSanityGraphqlReferences)), | |
} | |
} | |
// If it's any other value (primitive), keep the value as it is | |
return { | |
...(await acc), | |
[key]: value, | |
} | |
}, {}) | |
} |
I'm looking to solve the same problem but in the SSR realm on GatsbyJS.
References are also truncated during the build, I've seen a few client-layer solutions... but these values are necessary during the static page generation.
OMG, found the issue! You can get PortableText references to populate more data by boosting the resolveReferences
depth higher than you might expect.
I think you could use something like this, dont you?
_rawTestimonials(resolveReferences: { maxDepth: 5 })
where "testimonials" is an array with references in my schema and those references hold images, which are also references. So its a few levels deep. You can use the _raw* accessor of the field and tell sanity how much to traverse into.
I created optimised version with single groq query (we had >100k requests per day because of this). It's not so beautiful but it works.
export const getResolveSanityGraphqlReferences = async <T = unknown>(data: T): Promise<T> => {
if (typeof data !== 'object' || !data) {
return data;
}
const references = {};
const findReferences = (value, key: string) => {
if (value?._type === 'reference') {
references[key] = value._ref;
}
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
Object.keys(value).forEach((nestedKey) => findReferences(value[nestedKey], `${key}.${nestedKey}`));
}
if (Array.isArray(value)) {
value.forEach((item, index) => findReferences(item, `${key}[${index}]`));
}
};
Object.keys(data).forEach((key) => findReferences(data[key], key));
if (Object.keys(references).length === 0) {
return data;
}
const resolvedReferences = await client.fetch(
groq`*[_id in $refs]{
_id,
link,
}`,
{
refs: Object.values(references),
},
);
const resolveReferences = (value: any, key: string) => {
if (references[key]) {
return resolvedReferences.find((ref) => ref._id === references[key]);
}
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
return Object.keys(value).reduce<any>(
(acc, nestedKey) => ({
...acc,
[nestedKey]: resolveReferences(value[nestedKey], `${key}.${nestedKey}`),
}),
{},
);
}
if (Array.isArray(value)) {
return value.map((item, index) => resolveReferences(item, `${key}[${index}]`));
}
return value;
};
return Object.keys(data).reduce<any>(
(acc, key) => ({
...acc,
[key]: resolveReferences(data[key], key),
}),
{},
);
};
Tired of the Sanity GraphQL API not resolving references in your raw portable text (with no option to resolve the references)?
In my case this meant that internal links were a reference, and I couldn't access the slug of the post which was being linked to. Here is a solution which you can use inside a library like Next.JS with something like
getStaticProps
(or on the client, although this will make your site slower).When you have fetched the data for a document, you can pass it through this function, and it will resolve the unresolved references for you. Since it will run on build-time (if you use
getStaticProps
in Next.JS), it will not make your site any slower. The extra time is added to the build of your site