groqd icon indicating copy to clipboard operation
groqd copied to clipboard

No longer possible to get resultSourceMap from sanity client.

Open devinatbryt opened this issue 7 months ago • 6 comments

Is there an existing issue for this?

  • [x] I have searched the existing issues

Code of Conduct

  • [x] I agree to follow this project's Code of Conduct

Code Sandbox link

No response

Bug report

When enabling visual editing you have to serialize the data within your html markup. It seems that when using the "makeSafeQueryRunner" function, there is no way to allow the function to return anything other than the actual data. This makes it impossible to use with the visual editor.

Maybe additional configuration could be added to allow for this?

devinatbryt avatar Apr 29 '25 03:04 devinatbryt

Can you give mea code sample, and a link to the relevant docs, so i can understand this feature better?

scottrippey avatar May 01 '25 02:05 scottrippey

Hey @scottrippey , sorry for the delay here! I don't know why I forgot to include the referrence and provide more context!

I'm just reading based off of the normal sanity integration tutorial for an astro project.

One thing you cannot do with the existing groqd is set filterResponse to false and receive the source map.

Normal integration example:

// load-query.ts
import {type QueryParams} from 'sanity'
import {sanityClient} from 'sanity:client'

const visualEditingEnabled = import.meta.env.PUBLIC_SANITY_VISUAL_EDITING_ENABLED === 'true'
const token = import.meta.env.SANITY_API_READ_TOKEN

export async function loadQuery<QueryResponse>({
  query,
  params,
}: {
  query: string
  params?: QueryParams
}) {
  if (visualEditingEnabled && !token) {
    throw new Error(
      'The `SANITY_API_READ_TOKEN` environment variable is required during Visual Editing.',
    )
  }

  const perspective = visualEditingEnabled ? 'drafts' : 'published'

  const {result, resultSourceMap} = await sanityClient.fetch<QueryResponse>(query, params ?? {}, {
    filterResponse: false,
    perspective,
    resultSourceMap: visualEditingEnabled ? 'withKeyArraySelector' : false,
    stega: visualEditingEnabled,
    ...(visualEditingEnabled ? {token} : {}),
    useCdn: !visualEditingEnabled,
  })

  return {
    data: result,
    sourceMap: resultSourceMap,
    perspective,
  }
}

If you try to do something similar with the client groqd you'll get an error becausee the makeSafeQueryRunner is specifically looking for the query data to be returned.

import type * as SanityTypes from "./sanity.types.ts";

import { sanityClient } from "sanity:client";
import { createGroqBuilder, makeSafeQueryRunner } from "groqd";

const visualEditingEnabled =
  import.meta.env.PUBLIC_SANITY_VISUAL_EDITING_ENABLED === "true";
const token = import.meta.env.SANITY_API_READ_TOKEN;

const perspective = visualEditingEnabled ? "drafts" : "published";

// Throws an error because it's expecting only the data to be returned.
export const runQuery = makeSafeQueryRunner(async (query, { parameters }) => {
  if (visualEditingEnabled && !token) {
    throw new Error(
      "The `SANITY_API_READ_TOKEN` environment variable is required during Visual Editing.",
    );
  }

  const {result, resultSourceMap} = await sanityClient.fetch(query, parameters, {
    filterResponse: false,
    perspective,
    resultSourceMap: visualEditingEnabled ? "withKeyArraySelector" : false,
    stega: visualEditingEnabled,
    ...(visualEditingEnabled ? { token } : {}),
    useCdn: !visualEditingEnabled,
  });

  return {
    data: result,
    sourceMap: resultSourceMap,
    perspective,
  }
});

type SchemaConfig = {
  schemaTypes: SanityTypes.AllSanitySchemaTypes;
  referenceSymbol: typeof SanityTypes.internalGroqTypeReferenceTo;
};

export const q = createGroqBuilder<SchemaConfig>({});

I hope this helps provide additional context, thanks for looking into this, I really appreciate any assistance or guidance you can provide :) !

devinatbryt avatar May 01 '25 22:05 devinatbryt

Thank you for the context! Ironically, @BrianWalters had this same problem 2 years ago and I just came across his workaround. I would be happy to create an alternative to makeSafeQueryRunner, that allows arbitrary return values. The hardest part at this point is coming up with a name! Any suggestions?

scottrippey avatar May 02 '25 23:05 scottrippey

Names are always the hardest part 😆 !

Here's a list of names:

  1. makeSafeQueryWrapper
  2. makeSafeQueryWithContext

I forgot to send this message yesterday!

devinatbryt avatar May 03 '25 15:05 devinatbryt

I've been playing around with some possible syntax, and I've come to a few options. Open for feedback.

First let's assume that the runQuery should allow for extra inputs (eg. tags or draftMode).

type ExtraInputs = { tags: string[], perspective: string };

Let's also assume that we can include extra fields in the output (eg. resultSourceMap or more).

type ExtraOutputs = { resultSourceMap: unknown };

So here's my proposed syntax. I'd like to continue supporting a "just the results" method, in addition to a "results with the metadata". And I can't think of a good way to do this within a single function, so I'm proposing 2 functions:

  • createQueryRunner to replace makeSafeQueryRunner ... it's the exact same, just renamed.
  • createQueryRunnerExtended to support the "with metadata" approach:
const runQuery = createQueryRunnerExtended<ExtraInputs, ExtraOutputs>((query, inputs) => {
  const {
    parameters, // you'll ALWAYS get the `parameters` as an input, in addition to ExtraInputs
    tags,
    perspective,
  } = inputs;

  // fetch the data as normal:
  const { result, resultSourceMap } = await sanityFetch(query, { params: parameters, tags, perspective });

  return { 
    result, // you must ALWAYS return the `result`, in addition to the ExtraOutputs
    resultSourceMap,
  };
})

The main advantage of these functions is:

  • Automatically calls "parse"
  • Strongly-typed parameters field -- it's optional if there are no params, it's required if there are.
  • Handles all the types for you ... you only need to specify your extra inputs / outputs.

Open for feedback!

scottrippey avatar May 06 '25 20:05 scottrippey

I absolutely love this idea 😄 ! I can't see any negatives with how createQueryRunnerExtended is meant to function! My only thought for the naming of createQueryRunnerExtended is the naming, maybe it could be createExtendedQueryRunner?

devinatbryt avatar May 06 '25 22:05 devinatbryt