Last active
May 7, 2024 10:17
-
-
Save employee451/2f05d49b5d8251e2acb25fe408426b83 to your computer and use it in GitHub Desktop.
Resolve references in Sanity data (GraphQL)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | |
} | |
}, {}) | |
} |
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),
}),
{},
);
};
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.