flat-fuchsia
flat-fuchsia2w ago

Axios interceptor, authProvider onError still throws error

i implemented axios as authProvider, but i also added interceptor to refresh access token, i do refresh the token set the new token in the error.config but new problem is onError throws an error now on the screen saying that TypeError: Cannot destructure property 'data' of '(intermediate value)' as it is undefined. at Object.deleteOne (provider.ts:150:13)
3 Replies
kapa.ai
kapa.ai2w ago
Hi @Onder. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- It seems like you are encountering an issue with the onError method in your authProvider when using Axios interceptors to refresh the access token. The error message indicates that there is a problem with destructuring the data property from an undefined value in your deleteOne method. Here are a few steps to help you troubleshoot and resolve this issue: 1. Ensure Axios Interceptor is Correctly Configured: Make sure your Axios interceptor is correctly set up to handle token refresh and retry the original request. Here is an example from the knowledge sources:
axiosInstance.interceptors.request.use((request: AxiosRequestConfig) => {
const token = localStorage.getItem("accessToken");
if (request.headers) {
request.headers["Authorization"] = `Bearer ${token}`;
} else {
request.headers = {
"Content-Type": "application/json; charset=utf-8",
Accept: "application/json",
Authorization: `Bearer ${token}`,
};
}
return request;
});

axiosInstance.interceptors.response.use(
(response) => {
return response;
},
async (error) => {
const originalRequest = error.config;
if (error.response.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
// Logic to refresh the token
const newToken = await refreshToken();
localStorage.setItem("accessToken", newToken);
axios.defaults.headers.common["Authorization"] = `Bearer ${newToken}`;
return axiosInstance(originalRequest);
}
return Promise.reject(error);
}
);

axiosInstance.interceptors.request.use((request: AxiosRequestConfig) => {
const token = localStorage.getItem("accessToken");
if (request.headers) {
request.headers["Authorization"] = `Bearer ${token}`;
} else {
request.headers = {
"Content-Type": "application/json; charset=utf-8",
Accept: "application/json",
Authorization: `Bearer ${token}`,
};
}
return request;
});

axiosInstance.interceptors.response.use(
(response) => {
return response;
},
async (error) => {
const originalRequest = error.config;
if (error.response.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
// Logic to refresh the token
const newToken = await refreshToken();
localStorage.setItem("accessToken", newToken);
axios.defaults.headers.common["Authorization"] = `Bearer ${newToken}`;
return axiosInstance(originalRequest);
}
return Promise.reject(error);
}
);

2. Implement onError Method in authProvider: Ensure that your onError method in the authProvider is correctly implemented to handle errors and provide the necessary properties in the return object. Here is an example from the knowledge sources:
const authProvider = {
onError: async (error) => {
if (error.status === 401 || error.status === 403) {
return {
redirectTo: "/login",
logout: true,
error: error,
};
}
return {};
},
};

const authProvider = {
onError: async (error) => {
if (error.status === 401 || error.status === 403) {
return {
redirectTo: "/login",
logout: true,
error: error,
};
}
return {};
},
};

3. Check the deleteOne Method: Ensure that the deleteOne method in your data provider correctly handles the response and error cases. Here is an example snippet from the knowledge sources:
export const dataProvider = (apiUrl: string): DataProvider => ({
getList: async ({ resource, pagination, sorters, filters }) => {
const url = `${apiUrl}/${resource}`;
const queryFilters = generateFilters(filters);
const { data: response, headers } = await axiosInstance.get(`${url}?cursor=${pagination?.current || 0}&${stringify(queryFilters)}`);
return {
data: response.data,
total: headers["x-total-count"],
};
},
deleteOne: async ({ resource, id }) => {
const url = `${apiUrl}/${resource}/${id}`;
const { data } = await axiosInstance.delete(url);
return {
data,
};
},
});

export const dataProvider = (apiUrl: string): DataProvider => ({
getList: async ({ resource, pagination, sorters, filters }) => {
const url = `${apiUrl}/${resource}`;
const queryFilters = generateFilters(filters);
const { data: response, headers } = await axiosInstance.get(`${url}?cursor=${pagination?.current || 0}&${stringify(queryFilters)}`);
return {
data: response.data,
total: headers["x-total-count"],
};
},
deleteOne: async ({ resource, id }) => {
const url = `${apiUrl}/${resource}/${id}`;
const { data } = await axiosInstance.delete(url);
return {
data,
};
},
});

By following these steps, you should be able to handle the token refresh and error handling correctly, avoiding the TypeError you encountered. For more detailed information, you can refer to the following documentation: - Authentication Error Handling (https://refine.dev/docs/guides-concepts/authentication/#error-handling) - Auth Provider onError Method (https://refine.dev/docs/authentication/auth-provider/#onerror) If you need further assistance, please ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2w ago
<@238969575289389057> kapa.ai is still learning and improving, please let us know how it did by reacting below
flat-fuchsia
flat-fuchsia2w ago
i discovered that setFilters, setCurrent and setPageSize functions of useTable are not wrapped with useCallback, causing infinite rerenders when used inside useEffect. I use useRef to workaround