SimeonGriggs / sanity-remix-template

Sanity Studio v3 embedded into a Remix Vite application configured for Vercel hosting with visual editing
https://sanity-remix-template.sanity.build
164 stars 35 forks source link

Error in @sanity/client with Remix? #30

Closed 58bits closed 11 months ago

58bits commented 1 year ago

A quick note here with more info when I have time.

I just spent hours trying to debug an issue on a Remix route and request for loader data, with the browser fetch request (to Remix sever - not the loader call) to Sanity left in a 'pending' state - and never returning.

I tried pretty much everything I could to debug the Sanity client api call...

export const loader: LoaderFunction = async ({ request, params }: LoaderArgs) => {
  invariant(params.slug, 'Expected params.slug')
  const query = groq`*[_type == "guide" && slug.current == $slug][0] {
        _id,
        title,
        "slug": slug.current,
        body[]{
          ...,
          markDefs[]{
            ...,
            _type == "internalLink" => {
              "slug": @.reference->slug
            }
          }
        },
        image{
          asset->{
            _id,
            url
          }
        },
      }`

let content
try {
    content = await getClient()
      .fetch(query, params)
      .then(res => {
        return res ? res : null
      })
      .catch(error => {
        console.log(error)
      })
  } catch (error) {
    console.log(error)
  }

... 

.. including stepping into the client code, but I found it very difficult to determine what was going on. It sometimes albeit infrequently worked - but mostly failed - swallowing any errors that may have been generated (assuming there were any).

I switched to debugging the queries with a direct GET api call...

export const loader: LoaderFunction = async ({ request, params }: LoaderArgs) => {
  invariant(params.slug, 'Expected params.slug')
  const query = `*[_type == "guide" && slug.current == "${params.slug}"][0] {
        _id,
        title,
        "slug": slug.current,
        body[]{
          ...,
          markDefs[]{
            ...,
            _type == "internalLink" => {
              "slug": @.reference->slug
            }
          }
        },
        image{
          asset->{
            _id,
            url
          }
        },
      }`

  const queryEncoded = encodeURIComponent(query)
  const response = await fetch(
    `https://<project-id>.api.sanity.io/v2022-09-19/data/query/production?query=${queryEncoded}`
  )
  if (!response.ok) {
    if (response.status === 404) {
      throw new Response('Not Found', { status: 404 })
    } else {
      throw new Response('An error occurred', { status: response.status })
    }
  }

  const data = await response.json()
  const content = data.result
  if (!content) {
    throw new Response('Not Found', { status: 404 })
  }

  return json({ content})
}

...and everything worked fine.

The exact same query via groq and using the sanity client - failed - almost all of the time.

Wondering if it may have something to do with the SSR render and whether the sanity client is behaving differently between renders.

Will try to send more when I have time.

SimeonGriggs commented 1 year ago

Which version of sanity client, and was this working locally or at a deployment target — and if the latter which one?

58bits commented 1 year ago

@sanity/client 5.2.1 - and this was all locally during development (although using a 'production' dataset if I've understood the question above?)

The exact same groq query works fine both in raw form submitted via direct GET api and querytring above, as well is in the 'vision' tool in Studio.

Just to recap - here are the two versions of the loader function - the first below - using @sanity/client - never returns, while last one below works fine.

--- @sanity/client

export const loader: LoaderFunction = async ({ params }: LoaderArgs) => {
  invariant(params.slug, 'Expected params.slug')
  const query = groq`*[_type == "guide" && slug.current == $slug][0] {
    _id,
    title,
    "slug": slug.current,
    body[]{
      ...,
      markDefs[]{
        ...,
        _type == "internalLink" => {
          "slug": @.reference->slug
        }
      }
    },
    image{
      asset->{
        _id,
        url
      }
    },
  }`

  let content
  try {
    content = await getClient()
      .fetch(query, params)
      .then(res => {
        return res ? res : null
      })
      .catch(error => {
        console.log(error)
      })
  } catch (error) {
    console.log(error)
  }

  if (!content) {
    throw new Response('Not Found', { status: 404 })
  }

  return json({content})
}

--- GET API querystring

export const loader: LoaderFunction = async ({ params }: LoaderArgs) => {
  invariant(params.slug, 'Expected params.slug')
  const query = `*[_type == "guide" && slug.current == "${params.slug}"][0] {
    _id,
    title,
    "slug": slug.current,
    body[]{
      ...,
      markDefs[]{
        ...,
        _type == "internalLink" => {
          "slug": @.reference->slug
        }
      }
    },
    image{
      asset->{
        _id,
        url
      }
    },
  }`

  const queryEncoded = encodeURIComponent(query)
  const response = await fetch(
    `https://hsvi6ssr.api.sanity.io/v2022-09-19/data/query/production?query=${queryEncoded}`
  )
  if (!response.ok) {
    if (response.status === 404) {
      throw new Response('Not Found', { status: 404 })
    } else {
      throw new Response('An error occurred', { status: response.status })
    }
  }

  const data = await response.json()
  const content = data.result

  if (!content) {
    throw new Response('Not Found', { status: 404 })
  }

  return json({content})
}

I'll try updating the project deps now, including upgrading the @sanity/client to 5.2.2.

58bits commented 1 year ago

Okay so perhaps this will help. I just tried a production build of the app and the @sanity/client version works! (@sanity/client 5.2.1)

58bits commented 1 year ago

Still the same even after updating all dependencies. I'd be glad to send you a Loom screen recording showing the problem, the code, the pending client state, and the before / after versions - if you think it might help (although would prefer to send this via a private message or email).

58bits commented 1 year ago

For now, since we're only querying Sanity from the server, we've solved this by using the API URLs and query string for groq queries.

We created a simple api URL helper...

export const getApiUrl = (query: string, useCdn?: boolean): string => {
  const { projectId, apiVersion, dataset } = projectDetails()
  const queryEncoded = encodeURIComponent(query)
  // Default to no CDN during development and use CDN
  // in any other environment - unless overridden via useCdn
  let api
  if (useCdn !== undefined) {
    api = useCdn ? 'apicdn' : 'api'
  } else {
    if (process.env.NODE_ENV && process.env.NODE_ENV === 'development') {
      api = 'api'
    } else {
      api = 'apicdn'
    }
  }
  const apiUrl = `https://${projectId}.${api}.sanity.io/v${apiVersion}/data/query/${dataset}?query=${queryEncoded}`
  return apiUrl
}
SimeonGriggs commented 1 year ago

A Loom might be helpful, can't say I've seen this issue before.

58bits commented 1 year ago

Hi @SimeonGriggs - I 'de-branded' the project - so I can share the link here...

https://www.loom.com/share/8f5419163e89456290b6315218c4bade

Hope this helps.

58bits commented 1 year ago

Also if this helps, I'd be glad to jump on a call and screen-share - taking you through the steps to reproduce this. Just to re-cap - this only occurs in development. In production - it all works. So I'm guessing it's something to do with the way Remix is compiling/bundling the server bundle during development.