Handling unauthorized request in react-query
Asked Answered
T

4

14

I'm having problems using react-query. Whenever the response from the server is Unauthorized, useQuery is returning flags status='success' and isError=false. The server response status is 401 and the content of the json response is { error: true, message: 'UNAUTHORIZED' }. I didn't customize react-query in any way.

I'm not using the ReactQueryConfigProvider, or passing any options in the call to customize the behaviour.

This is the call:

const { status, data, error } = useQuery(
  ["hotelsList", { token: token }],
  getHotels
);

And this is the service:

const getHotels = async ({ token }) => {
  const uri = process.env.REACT_APP_API_ENDPOINT_v2 + `/hotels`
  return (await fetch(uri, {
    method: "get",
    headers: {
      Authorization: "Bearer " + token,
      "Content-Type": "application/json"
    }
  })).json()
}

Being the token invalid, the server is responding with an 401 status code and my custom json response.

Response code from server is 401

This is the data and error objects from react-query.

Objects returned by useQuery

Tympanist answered 21/7, 2020 at 13:1 Comment(0)
R
9

React-query is agnostic about your API that error is only supposed to change Is when your API doesn't respond that's when you will get an error but if the server responds with 404 or 401 etc react-query won't care because it's a valid response, react-query is only the gateway for the response it doesn't handle it in useQuery, I have made a demo here: you can see that the URL is wrong (points to a 404) and it's returning 404 it won't affect the error value but try to change the API URL to github.x for example, it will fail cause no response has been returned and the error value will change

CodeSandbox Demo

Renarenado answered 21/7, 2020 at 13:29 Comment(4)
Thanks for pointing me in the right direction. I added my own response for people in my exact situation, but marked yours as Correct since it's the most important to understand this issue.Tympanist
Could you update the Sandbox link? The link is not available.Exceptionable
@SherlyFebrianti thank you for pointing this out i updated the link for the codesandbox demoRenarenado
the link has died againFirebox
T
24

To expand on Chamsddine response, i needed to throw an error when fetch response was not ok. This enabled all the error flags on react-query.

const response = await fetch(uri, {
  method: "get",
  headers: {
    Authorization: "Bearer " + token,
    "Content-Type": "application/json"
  }
})
if (!response.ok) throw new Error(response.statusText)
return await response.json()
Tympanist answered 21/7, 2020 at 14:3 Comment(1)
This should have been the accepted answer. Refer to the following paragraph from react-query documentation (react-query.tanstack.com/guides/…): For React Query to determine a query has errored, the query function must throw.Hydrology
R
9

React-query is agnostic about your API that error is only supposed to change Is when your API doesn't respond that's when you will get an error but if the server responds with 404 or 401 etc react-query won't care because it's a valid response, react-query is only the gateway for the response it doesn't handle it in useQuery, I have made a demo here: you can see that the URL is wrong (points to a 404) and it's returning 404 it won't affect the error value but try to change the API URL to github.x for example, it will fail cause no response has been returned and the error value will change

CodeSandbox Demo

Renarenado answered 21/7, 2020 at 13:29 Comment(4)
Thanks for pointing me in the right direction. I added my own response for people in my exact situation, but marked yours as Correct since it's the most important to understand this issue.Tympanist
Could you update the Sandbox link? The link is not available.Exceptionable
@SherlyFebrianti thank you for pointing this out i updated the link for the codesandbox demoRenarenado
the link has died againFirebox
S
1

For errors like 401

you should use a global handling style, I mean here is the best solution IMHO

I am attaching the code from the link just in case the link dies for some reason

  const queryClient = new QueryClient({
    defaultOptions: {
        queries: {
            retry: (failureCount, err) => {
                if (err.response?.status === 401) {
                    return false; // do not retry, trigger error
                }

                // otherwise, restore default
                const defaultRetry = new QueryClient().getDefaultOptions().queries?.retry;

                return Number.isSafeInteger(defaultRetry) ? failureCount < (defaultRetry ?? 0) : false;
            },

            onError: err => {
                if (err.response?.status === 401) {
                    // clean up session state and prompt for login
                    // ex: window.location.reload();
                }
            }
        }
    }
});


ReactDOM.render(
    <React.StrictMode>
            <QueryClientProvider client={queryClient}> //here is your client with default options
                <BrowserRouter>
                    <AppContextProvider>
                        <App />
                    </AppContextProvider>
                </BrowserRouter>
            </QueryClientProvider>
    </React.StrictMode>,
    document.getElementById('root')
);

in case you use window.fetch (not relevant for axios) you should check err.status instead of err.response?.status and be sure that you throw on cath in your window.fetch client

.catch(response => {throw response;});
Satchel answered 1/10, 2023 at 10:25 Comment(0)
H
0

None of the solutions works with Typescript, what I did was:

 const { isLoading, error, data } = useCampaign()
  let myCustomErrorLet: any = {}
  myCustomErrorLet.data = error
  console.log(myCustomErrorLet?.data?.response);
Hulbard answered 31/8, 2020 at 8:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.