groqd icon indicating copy to clipboard operation
groqd copied to clipboard

How to use sanityImage('string') in q.object?

Open multiplehats opened this issue 2 years ago • 4 comments

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?

multiplehats avatar Aug 14 '23 12:08 multiplehats

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().

gksander avatar Aug 22 '23 18:08 gksander

@multiplehats any solution? I'm facing the same issue.

tutods avatar Aug 29 '23 06:08 tutods

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.

lime avatar Sep 28 '23 08:09 lime

I'm having the same issue, I had like to find a better solution for this aswell

plckr avatar Jul 17 '24 09:07 plckr