Error handling axios request with recursion - reactjs

I have a use effect with the helper function registerAccountInConfigurator(token)
acquireTokenForScope([`${process.env.REACT_APP_SW_SCOPE}`]).then(
(token) => {
if (token) {
// console.log(registerAccountInConfigurator(token));
registerAccountInConfigurator(token).then(function (response) {
console.log("response arrived ", response);
});
}
return null;
}
);
In case of an error in the helper function I want to do some steps and then call the function again.
export async function registerAccountInConfigurator(
adb2cToken: string,
change: any = false,
account?: any
) {
try {
const contextRes = await axios.get(
`${process.env.REACT_APP_SW_BASE_URL}/context`,
{
headers: change
? {
"sw-access-key": process.env.REACT_APP_SW_ACCESS_KEY ?? "",
...(adb2cToken !== "" && {
Authorization: `Bearer ${adb2cToken}`,
}),
}
: {},
}
);
const context: any = { ...contextRes.data };
const response = await axios.post(
`${process.env.REACT_APP_SW_BASE_URL}/post-ecommerce/account/register`,
account ?? { storefrontUrl: window.origin },
{ headers: headers(context.token, adb2cToken) }
);
const newToken = response.headers["sw-context-token"];
localStorage.setItem(SW_CONTEXT_TOKEN, newToken);
return response.data;
// Promise.resolve(response.data);
// return new Promise((resolve, reject) => {
// return resolve(response.data);
// });
} catch (error) {
console.log(error);
// do some steps
await registerAccountInConfigurator(adb2cToken, true);
}
}
To artficially test it with an 401 Error I use the paramater change which is per default false and will be set to true in the catch block.
What I dont understand is when I call console.log("response arrived ", response); with setting change to true. I get a normal response, an object. When I set change to false, triggering the catch block, I get undefined as the response value in console.log("response arrived ", response); why is that happening? How can i change that?

Related

Axios.get not returning any data

I can not get the right info on my API.
i tried this and nothing comes back
const res = () => {
axios.get('https://api.scripture.api.bible/v1/bibles', {
headers: {
'api-key': '5b5d4503884b7a2515e8cee8f4b00746',
},
})
}
Your code works fine, but you are not doing anything with the response. axios.get() returns a Promise, so you need to handle it using .then()
const res = () => {
axios.get("https://api.scripture.api.bible/v1/bibles", {
headers: {
"api-key": "5b5d4503884b7a2515e8cee8f4b00746"
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
};
res();
or make an async function and use async await.
const res = async () => {
try {
const response = await axios.get("https://api.scripture.api.bible/v1/bibles", {
headers: {
"api-key": "5b5d4503884b7a2515e8cee8f4b00746"
}
});
console.log(response);
} catch (error) {
console.log(error);
}
};
res();
Instead of console.logging you can do anything, for example use a callback function:
const res = async (callback, errorCallback) => {
try {
const response = await axios.get("https://api.scripture.api.bible/v1/bibles", {
headers: {
"api-key": "5b5d4503884b7a2515e8cee8f4b00746"
}
});
callback(response);
} catch (error) {
errorCallback(error);
}
};
const onSuccess = result => {
const data = JSON.stringify(result);
alert(data)
};
const onError = error => {
alert(`Ops! An error occured: ${error}`);
};
res(onSuccess, onError);

Apollo onError forward(operation) not working

I used the example form the Apollo docs:
onError(({ graphQLErrors, networkError, operation, forward }) => {
if (graphQLErrors) {
for (let err of graphQLErrors) {
switch (err.extensions.code) {
// Apollo Server sets code to UNAUTHENTICATED
// when an AuthenticationError is thrown in a resolver
case 'UNAUTHENTICATED':
// Modify the operation context with a new token
const oldHeaders = operation.getContext().headers;
operation.setContext({
headers: {
...oldHeaders,
authorization: getNewToken(),
},
});
// Retry the request, returning the new observable
return forward(operation); }
}
}
// To retry on network errors, we recommend the RetryLink
// instead of the onError link. This just logs the error.
if (networkError) {
console.log(`[Network error]: ${networkError}`);
}
});
But nothing happens. The second request never works. Maybe it's problem with react native?
try this:
export const logoutLink = onError(({ networkError, operation, forward }) => {
if (networkError?.statusCode === 401) {
return new Observable(observer => {
(async () => {
try {
const newToken = await getToken();
// Modify the operation context with a new token
const oldHeaders = operation.getContext().headers;
operation.setContext({
headers: {
...oldHeaders,
authorization: `Bearer ${newToken}`,
},
});
const subscriber = {
next: observer.next.bind(observer),
error: observer.error.bind(observer),
complete: observer.complete.bind(observer),
};
// Retry last failed request
forward(operation).subscribe(subscriber);
} catch (error) {
observer.error(error);
}
})();
});
}
});

How to return API data to a separate component - React Native

I am Fetching data from an API in my Native App and displaying it as a List.
Below is my code:
async componentWillMount() {
if (Platform.OS === 'android') {
BackHandler.addEventListener('hardwareBackPress', this.backPressed);
}
this.fetchNotifications();
}
}
async fetchNotifications() {
this.setState({refreshing: true});
const config = getAppConfig();
const cognitoToken = await this.getCognitoToken(config);
if (cognitoToken !== null) {
let headers = await this.getRequestHeaders(cognitoToken);
let body = this.getRequestBody(config);
let notificationUrl = config["notification-retrieve-api"];
return fetch(notificationUrl,
{
method: 'POST',
headers: headers,
body: body
}).then((response) => {
if (response.ok) {
return response.json();
} else {
throw new Error('Something went wrong');
}
})
.then((notifications) => {
console.log(JSON.stringify(notifications));
this.setState({
notifications,
error: null,
refreshing: false
});
}).catch((error) => {
this.setState({
notifications: [],
error,
refreshing: false
});
});
}
}
This works fine. I can retrieve the data from the API.
Now I want to separate the API code from my screen component. I will be calling "fetchNotifications" as a function in my screen component. I am trying to do so but it's not working at all.
This is what I'm doing:
async componentWillMount() {
if (Platform.OS === 'android') {
BackHandler.addEventListener('hardwareBackPress', this.backPressed);
}
let response = fetchNotifications();
this.setState({
notifications: response,
error: null,
refreshing: false
})
}
}
async function fetchNotifications() { //now this function is in another component
.
.
.
.
if(cognitoToken !== null) {
let headers = await this.getRequestHeaders(cognitoToken);
let body = this.getRequestBody(config);
let notificationUrl = config["notification-retrieve-api"];
return fetch(notificationUrl,
{
method: 'POST',
headers: headers,
body: body
}).then((response) => {
if (response.ok) {
response.json();
} else {
throw new Error('Something went wrong');
}
})
.then((response) => {
return response;
}).catch((error) => {
this.setState({
notifications: [],
error,
refreshing: false
});
});
}
}
export default fetchNotifications;
Is this way correct? Anyone with a better solution?
My two cents, I always put async task in Promise, including API requests.
// API helper file
export const fetchNotifications = (params) => {
return new Promise(async (resolve, reject)=>{
try{
const headers = getHeaders(params)
const body = getBody(params)
const response = await fetch(notificationUrl,
{
method: 'POST',
headers: headers,
body: body
})
if (response.ok) {
const responseObj = await response.json();
resolve(responseObj)
} else {
throw new Error('Something went wrong');
}
} catch (e) {
// something went wrong
generalHandler(e) // logging etc.
reject(e) // for ui handling
}
}
}
then we can use it everywhere
import { fetchNotifications } from '.../APIHelper'
In your ui file :
componentWillMount() {
fetchNotifications(params)
.then((notifications) => {
console.log(JSON.stringify(notifications));
this.setState({
notifications,
error: null,
refreshing: false
});
}).catch((error) => {
this.setState({
notifications: [],
error,
refreshing: false
});
});
}

Generic function to request api with Axios

I am trying to build a generic function for my endpoints, using Axios and React. Generic because I have always the same header and I do not want to repeat a lot of code for each of my components.
To do that, I built this function (sorry, a lot of comments that I will remove after of course) :
export const getRequest = ( endpoint ) => axios
.get( env._URL_SERVER_ + endpoint, { headers: getHeaders() } )
.then((res) => {
// Success
console.log(res);
return {error: false, response: res.data};
})
.catch((error) => {
// Error
if (error.response) {
/*
* The request was made and the server responded with a
* status code that falls out of the range of 2xx
*/
console.log(error.response.data);
console.log(error.response.status);
return {error: true, status: error.response.status, data: error.response.data};
} else if (error.request) {
/*
* The request was made but no response was received, `error.request`
* is an instance of XMLHttpRequest in the browser and an instance
* of http.ClientRequest in Node.js
*/
console.log(error.request);
return {error: true, data: error.request };
} else {
// Something happened in setting up the request and triggered an Error
console.log('Error', error.message);
return {error: true, data: error.message}
}
});
Ant then in my components I do that :
getSchools = () => {
this.setState({
loadingSchools: true
}, () => {
getRequest(`/schools?name=${this.state.filterByName}&city=${this.state.filterByCity}&school_type_id=${this.state.filterBySchoolTypeId}&page=${this.state.selectedPage}`)
.then((response) => {
// there is an error
if (!response.error) {
this.setState({
schools: response.response.data,
meta: response.response.meta,
links: response.response.links
})
} else {
this.setState({
error: true,
errorMessage: response.data,
})
}
})
.then(() => {
this.setState({loadingSchools : false});
})
})
}
It works fine. I tested it in several situation (all is OK - 200, not found - 404, no response). But is it a good practice ? I feel that there is a lot of codes in the parent component. Maybe I complicate my life?
Here is how I've done it:
var URL_BACKEND = "http://localhost:5000/";
// Create Function to handle requests from the backend
callToBackend = async (ENDPOINT, METHOD) => {
const options = {
url: `${URL_BACKEND}${ENDPOINT}`,
method: METHOD,
headers: {
Accept: "application/json",
"Content-Type": "application/json;charset=UTF-8",
},
};
const response = await axios(options);
return response.data;
}
// Then you make a call with the exact endpoint and method:
const response = await this.callToBackend('createSetupIntent', 'POST');
console.log(JSON.stringify(response));
create one common file for base URL let's say api.js
// api.js file code
export const apiUrl = axios.create({
baseURL: 'http://localhost:5000',
});
Register file
// register.js file code
import { apiUrl } from './api';
try {
const resp = await apiUrl.post('/api/register', {
username,
email,
password,
});
const { data, status } = resp;
if (Object.keys(data).length && status === 200) {
// received api data successfully
console.log('API response', data);
}
} catch (err) {
console.log(err);
}
// For auth request
try {
const token = localstorage.getItem('token');
const res = await apiUrl.post(
'/authroute',
{
name: fullName,
originCountry: country,
career: careerStatus,
},
{
headers: { Authorization: `Bearer ${token}` },
}
);
const { data, status } = strapiRes;
if (Object.keys(data).length && status === 200) {
return res.status(status).json(data);
}
} catch (error) {
throw new Error(error);
}
// same for all request
apiUrl.get(endpoint);
apiUrl.post(endpoint, body);
apiUrl.put(endpoint, body);
apiUrl.delete(endpoint, body);

useEffect wait for a result from the async function

I'm trying to create a function for POST request in React app (due to I need it in few places), it should return a responseText in the useEffect statement. the variant I've googled doesn't act as async - the string console.log("JSON", json) put into the console JSON undefined before the getting response from server...
useEffect(() => {
(async function() {
try {
const response = await post_return(postData);
const json = await JSON.stringify(response);
console.log("json", json);
} catch (e) {
console.error(e);
}
})();
}, [postData]);
const API_URL_REGISTRATION = "https:.....";
export function post_return (dataPost) {
var xhr = new XMLHttpRequest();
xhr.open("POST", API_URL_REGISTRATION, true);
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.onreadystatechange = function () {
if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
console.log("xhr.status", this.status);
console.log("this.responseText", this.responseText);
return xhr.status
}
};
xhr.onload = function () {
console.log("xhr.status", this.status);
console.log("this.responseText", this.responseText);
return xhr.status;
};
xhr.onerror = function () {
alert('Error' + this.status);
};
xhr.send(JSON.stringify(dataPost));
}
tried also:
export async function post_return (dataPost) {...
and:
xhr.onreadystatechange = async function ()
What I do wrong?
Thanks,
First thing that is wrong with post_return function is it returns undefined immediately, hence the response variable value is actually undefined and a result of calling JSON.stringify with undefined is also undefined. What you should do is to correct post_return so that it returns a Promise.
Simplest solution would be to use built-in fetch like so:
export function async post_return (dataPost) {
const response = await fetch(API_URL_REGISTRATION, {
method: 'POST',
body: JSON.stringify(dataPost),
headers: {
'Content-type': 'application/x-www-form-urlencoded'
}
});
if (response.ok) {
return response.json();
}
// Here you can do some basic error parsing/handling
throw new Error();
}
Rafal2228, thanks for your post, I adopted it for my needs
export async function post_return(url, dataPost) {
const response = await fetch(url, {
method: 'POST',
body: JSON.stringify(dataPost),
headers: {
'Content-type': 'application/x-www-form-urlencoded'
}
});
const json = await response.json();
return {
status: response.status,
body: json
};
}
useEffect(() => {
(async function() {
try {
if (postData.email !== undefined)
{
const response = await post_return(API_URL_REGISTRATION, postData);
// console.log("response", response);
setshowLoader(false);
if (response.status === 200) {
navigate("/login")
} else alert(response.body);
}
} catch (e) {
console.error('Ошибка ' , e);
}
})();
}, [postData]);

Resources