elysiajs / elysia

Ergonomic Framework for Humans
https://elysiajs.com
MIT License
10.11k stars 214 forks source link

Eden treaty swallows errors #651

Closed dominictobias closed 4 months ago

dominictobias commented 4 months ago

What version of Elysia.JS is running?

No response

What platform is your computer?

1.0.20

What steps can reproduce the bug?

Eden Treaty should not swallow errors, this makes error handling difficult and breaks the error handling of libraries like react-query.

Given the following treaty:

 const apiClient = treaty<App>(apiUrl, {
  onResponse: [
    (response: Response) => {
      if (!response.ok) {
        // tested to be definitely throwing
        throw new Error(`${response.statusText} (${response.status})`)
      }
      return response.json()
    },
  ],
})

And the following:

const {
    isLoading,
    error,
    data,
  } = useQuery('youtube/videos', () => 
    apiClient.youtube.videos.get({
      query: {
        order,
      },
    }),
  )

The error is never thrown to react-query and so error is undefined while data.error holds the error message.

If I use await I can see the error is never thrown by Treaty:

const {
    isLoading,
    error,
    data,
  } = useQuery('youtube/videos', async () => {
    try {
      await apiClient.youtube.videos.get({
        query: {
          order,
        },
      })
    } catch (err) {
      console.log('CAUGHT ERROR:', err) // Error thrown in onResponse never reaches here
      throw err
    }
  })

What is the expected behavior?

Errors thrown in onResponse should cascade so we can handle them in our apps

What do you see instead?

Errors are swallowed up by treaty and so the developer or libraries don't know if there was one without inspecting data.error which is undesired and causes libraries to not behave properly.

Could be a non-breaking change if there is a config option like throwOnError: true

Additional information

No response

dominictobias commented 4 months ago

I think I was able to achieve it using fetcher instead:

export const apiClient = treaty<App>(apiUrl, {
  async fetcher(url, options) {
    const res = await fetch(url, options)

    if (!res.ok) {
        throw new Error(`${res.statusText} (${res.status})`)
    }

    return res
  },
})