groqd
groqd copied to clipboard
How to use sanityImage('string') in q.object?
Hey guys! Thanks for the awesome library.
One thing I can't figure out for the life of me is how to use sanityImage inside q.object().
import { type Selection, type TypeFromSelection, q, sanityImage } from 'groqd';
const gqField = q.object({
s3_fileName: q.string().optional(),
s3_id: q.string().optional(),
createdAt: q.date(),
image: sanityImage('image')
// ^^ error: Type 'EntityQuery<FromSelection<{ readonly _key: ZodNullable<ZodString>; readonly _type: ZodString; }
// & { asset: EntityQuery<FromSelection<{ _ref: ZodString; _type: ZodLiteral<"reference">; }>>; }>>'
// is missing the following properties from type 'ZodType<any, any, any>': _type, _output, _input, _def, and 30 more.(2740)
})
const gqFields = {
desktop: q.array(gqField),
mobile: q.array(gqField),
} satisfies Selection;
export const gqPageFields = {
id: ['_id', q.string()],
name: q.string(),
_updatedAt: q.date(),
_createdAt: q.date(),
images: q.object({
one: q.object(gqFields),
two: q.object(gqFields),
})
} satisfies Selection;
export type PageFields = TypeFromSelection<typeof gqPageFields>;
Am I missing something?
Ahh – the Zod/TS errors are not at all comprehensible. The core issue, I believe, is that the fields inside q.object() are looking for zod-compatible types (like q.string(), q.number(), etc.), but the sanityImage helper actually returns back a Groqd EntityQuery instance (basically, a groq sub-query with associated schema). So, the sanityImage – as it stands – will only work inside of a .grab() statement.
I believe the rationale for returning a groqd query from sanityImage (instead of a more simple zod schema) is that to get image assets, you have to deref a field or two (I believe you have to do something like a asset→ call to get asset details. You can't do that without a groq query somewhere in there.
Not sure if it's feasible, but you could probably achieve this by refactoring to using nested .grab() calls instead of q.object().
@multiplehats any solution? I'm facing the same issue.
I think I got it working after a little trial and error, using nested .grab() calls per @gksander's suggestion.
So instead of this (which is how I expected groqd to work):
q("*")
.filter("_type == 'page'")
.grab$({
hero: q.object({
heading: q.string(),
image: sanityImage("image", { withAsset: ["base", "blurHash"] }),
content: q.contentBlocks(),
}),
sections: q.array(
q.object({
heading: q.string(),
image: sanityImage("image", { withAsset: ["base", "blurHash"] }),
content: q.contentBlocks(),
})
),
})
I used this:
q("*")
.filter("_type == 'page'")
.grab$({
hero: q("hero").grab$({
heading: q.string(),
image: sanityImage("image", { withAsset: ["base", "blurHash"] }),
content: q.contentBlocks(),
}),
sections: q("sections")
.filter() // turns it into an array query
.grab$({
heading: q.string(),
image: sanityImage("image", { withAsset: ["base", "blurHash"] }),
content: q.contentBlocks(),
}),
})
It seems to give me both the right data and types. Happy to hear suggestions if there are better approaches.
I'm having the same issue, I had like to find a better solution for this aswell