How can i make the interceptor run a function on error exept for one specific request? - axios

this is my interceptor:
axios.interceptors.response.use(
(response) => {
if (error.response?.status === 403) {
unstable_batchedUpdates(() => {
// to force react state changes outside of React components
useSnackBarStore.getState().show({
message: `${i18n.t('forbidden')}: ${error.toJSON().config.url}`,
severity: 'error',
})
})
}
return Promise.reject(error)
}
)
I want this behavior all the time except when I make this specific call or at least except every head call
export const companiesQueries = {
headCompany: {
name: 'headCompany',
fn: async (companyId) => {
return await axios.head(`/companies/${companyId}`)
},
},

fixed by applying these changes to the api call:
const uninterceptedAxiosInstance = axios.create()
headCompany: {
name: 'headCompany',
fn: async (companyId) => {
return await
uninterceptedAxiosInstance.head(`/companies/${companyId}`)
},
}

Related

How to use refetch method to send different queries using useQuery in react-query

I'm working with react-query in ReactJS to build a website.
I have the following code, where I fetch some data using useQuery():
const { error, data: movie, status, refetch } = useQuery({
queryKey: 'key1',
queryFn: async () => {
return await Promise.all([
axios.get(`API-1.com/get-some-data`), /*getting some data from api-1 */
axios.get(`API-2.com/get-some-data`), /*getting some data from api-2 */
]).then(([api1, ap2]) => {
return { data1: api1, data2: api2 }
})
}
})
The problem I'm facing is getting somtimes a 404 response from one of the apis, so I need to refetch the data just from the other api that doesn't cause the error.
I tried to use the refetch method inside onError, but it can't be used with parameters.
onError: (error) => {
refetch() /* refetch can't be used with parameters */
},
How can I handle this problem?
I ended up doing the following:
Writing the fetching part in an external function (so I can call the function with parameters)
Adding a try-catch block inside the queryFn to handle the errors
/********* this is the function where I fetch the data. *********/.
/********* with a param that tells me if i'll call API-2 or not *********/
const fetchData = async ({ isDataFoundInApi2 }) => {
return await Promise.all([
axios.get(`API-1.com/get-some-data`),
(!isDataFoundInApi2 && axios.get(`API-2.com/get-some-data`)),
]).then(([api1, api2]) => {
return { data1: api1, data2: api2 }
})
/********* the useQuery where the above method is called *********/
/********* first it sends true as a bool value, then if no *********/
/********* data was found in API-2, it sends false *********/
/********* from `catch` to prevent the fetching from API-2 *********/
const { error, data: movie, status } = useQuery({
queryKey: 'key1',
queryFn: async () => {
try {
return await fetchData({})
} catch (error) {
if (error.response?.status === 404)
return await fetchData({ isDataFoundInApi2: true }) // true means call only the first api
}
},
})

Redux toolkit createAsyncThunk using Parameter

I need your help.
We implemented createAsyncThunk using the Redux Toolkit.
However, as shown in the picture, I need a function to change ChannelId flexibly. Can't I use the parameter in createAsyncThunk?
How can I use it if I can?
Or is there any other way?
I am sorry that the quality of the question is low because I am Korean using a translator.
enter image description here
// ACTION
export const getYoutubeList_PlayList = createAsyncThunk(
"GET/YOUTUBE_PLAYLIST",
async (data, thunkAPI) => {
try {
const { data } = await axios.get<youtubeResponse>(
`https://www.googleapis.com/youtube/v3/playlists?key=${youTubeAcsses.apiKey}&channelId=${channelId}&part=snippet&maxResults=30`
)
return data
} catch (err: any) {
return thunkAPI.rejectWithValue({
errorMessage: '호출에 실패했습니다.'
})
}
}
);
// SLICE
const youtube_PlaylistSlice = createSlice({
name: "YOUTUBE_PLAYLIST",
initialState,
reducers: {},
// createAsyncThunk 호출 처리 = extraReducers
extraReducers(builder) {
builder
.addCase(getYoutubeList_PlayList.pending, (state, action) => {
state.loading = true;
})
.addCase(getYoutubeList_PlayList.fulfilled, (state, action: PayloadAction<youtubeResponse>) => {
state.loading = false;
state.data = action.payload;
})
.addCase(getYoutubeList_PlayList.rejected, (state, action: PayloadAction<any>) => {
state.error = action.payload;
});
},
});
You named both the incoming argument data as well as the result of your axios call. That will "shadow" the original data and you cannot access it any more. Give those two variables different names.
Here I called it arg, which allows you to access arg.channelId.
export const getYoutubeList_PlayList = createAsyncThunk(
"GET/YOUTUBE_PLAYLIST",
async (arg, thunkAPI) => {
try {
const { data } = await axios.get<youtubeResponse>(
`https://www.googleapis.com/youtube/v3/playlists?key=${youTubeAcsses.apiKey}&channelId=${arg.channelId}&part=snippet&maxResults=30`
)
return data
} catch (err: any) {
return thunkAPI.rejectWithValue({
errorMessage: '호출에 실패했습니다.'
})
}
}
);
You would now dispatch this as dispatch(getYoutubeList_PlayList({ channelId: 5 }))

Uncaught (in promise) TypeError: Cannot use 'in' operator to search for 'validateStatus' in

I am getting ** Uncaught (in promise) TypeError: Cannot use 'in' operator to search for 'validateStatus' in 5f8425a33a14f026f80133ed** where 5f8425a33a14f026f80133ed is the id passed to the axios url
I want to display the services based on the user id. My url works perfectly in postman but when i access it from the veux store it gives an error.
services.js (store)
import axios from 'axios';
const state = {
services : {},
status: '',
error: null
};
const getters = {
services : state => { return state.services }
};
const actions = {
async fetchServices({commit}, userId) {
let res = await axios.get('http://localhost:5000/api/services/displayUser' , userId)
commit('setProducts', res.data)
return res;
}
};
const mutations = {
setProducts (state, items) {
state.services= items
},
};
export default {
state,
actions,
mutations,
getters
};
This is how I am calling the action :
computed: {
...mapGetters(["services"]),
},
methods: {
...mapActions(["fetchServices"]),
getData(){
this.fetchServices(this.user._id)
},
},
async created() {
await this.getProfile();
await this.getData();
}
The axios route is defined as
router.get('/displayUser', (req,res) => {
const query = user = req.body ;
Services.find(query)
.exec((err, services) => res.json(services))
})
the error screenshot :
Error screenshot
GET request should not have a body. Either use query params, indicate an id in a path, or use POST request.
In case of query params this may look like this:
let res = await axios.get('http://localhost:5000/api/services/displayUser' , { params: { userId })
router.get('/displayUser', (req,res) => {
const query = user = req.query;
Services.find(query)
.exec((err, services) => res.json(services))
})
This worked for me too:
In front end: Vue Js
let res = axios.get("http://localhost:3000/api/v1/role/getRoleByName",
{ params: { roleName: "name of role you want to send as params" },
});
In back end: Node Js
router.get('/getRoleByName', (req,res)=>{
let roleName = req.query.roleName;
roleModule.getRoleByName(roleName).then(data =>{
response.json(res,data)
}
).catch(err=> {
response.badRequest(res, err);
})
});
it's a silly mistake axios.post req.
async addTodo({ commit }, title) {
try {
const res = await axios.post(BASE_URL, { title, complete: false });
commit("newTodo", res.data);
} catch (err) {
console.log(err.message);
}
},

How can I write conditional interceptor in Axios

I am able to add an interceptor for the Axios pipeline. Also, I need the loader to be conditional based. The situation is some requests can run in the background and don't need a loader to be blocking the UI. In such cases, I will be able to let the Axios know by sending an extra parameter saying isBackground call. How can I achieve this?
axios.interceptors.request.use((config) => {
this.isLoading = true; // Or trigger start loader
return config
}, (error) => {
this.isLoading = false // Or trigger stoploader
return Promise.reject(error)
})
axios.interceptors.response.use((response) => {
this.isLoading = false // Or trigger stoploader
return response
}, function(error) {
this.isLoading = false // Or trigger stoploader
return Promise.reject(error)
})
Just use your own custom property isBackground on the config like this:
axios.interceptors.request.use((config) => {
console.log(config.isBackground)
return config
}, (error) => {
console.log(error.config.isBackground)
return Promise.reject(error)
})
axios.interceptors.response.use((response) => {
console.log(response.config.isBackground)
return response
}, function(error) {
console.log(error.config.isBackground)
return Promise.reject(error)
})
const config = {
isBackground: true
}
axios.get('https://httpbin.org/get', config)
.then(response => {
console.log(response)
})
.catch(error => {
console.log(error)
})
Note that there is a bug in current release 0.19.0 waiting to be fixed, which breaks this functionality. Works ok in version 0.18...
Fiddle

Implementing redirect in Redux middleware

Let's say I have following action:
export function signIn(data) {
return {
type: USER_SIGN_IN,
promise: api.post('/sign_in', data)
}
}
and following middleware:
export default function promiseMiddleware() {
return next => action => {
const { promise, type, ...rest } = action
if (!promise) {
return next(action)
}
const SUCCESS = `${type}_SUCCESS`
const REQUEST = `${type}_REQUEST`
const ERROR = `${type}_ERROR`
next({ type: REQUEST, ...rest })
return promise
.then(res => {
next({ response: res.data, type: SUCCESS, ...rest })
return true
})
.catch(error => {
...
})
}
}
This code is loosely based on https://github.com/reactGo/reactGo/
But what if in then callback after calling next I want to make a redirect to another path?
I did following. I passed redirect url through action:
export function signIn(data) {
return {
type: USER_SIGN_IN,
promise: api.post('/sign_in', data),
redirect: '/'
}
}
and added another call of next method with push from react-router-redux.
import { push } from 'react-router-redux'
export default function promiseMiddleware() {
return next => action => {
const { promise, type, redirect, ...rest } = action
...
return promise
.then(res => {
next({ response: res.data, type: SUCCESS, ...rest })
next(push(redirect))
return true
})
.catch(error => {
...
})
}
}
It seems like it works, but I'm not sure if this is a good idea or if there are some pitfalls of multiple next calls and I shouldn't do like this?
Maybe there are some better approaches for implementing such redirects?