AccessToken from AsyncStorage + request by axios - react-native

While writing the application in the react-native I met with a certain obstacle related to promises, so I have a function that is responsible for the authorized request
export const authorizeRequest = async () => {
const token = await deviceStorage.getItem('accessToken');
return axios.create({
timeout: 2000,
headers: {
'Authorization': 'Bearer ' + token,
'Content-Type': 'application/json'
}
});
};
in order to get data from it I write code in the style
authorizeRequest().then(a => a.get('http://192.168.0.60:8080/users/echo2/asd')
.then(response => ToastAndroid.show('Response ' + response.data, ToastAndroid.SHORT))
.catch(error => ToastAndroid.show('error ' + JSON.stringify(error), ToastAndroid.LONG)))
Is it possible to avoid the first use of .then when calling authorizeRequest().then(....) so that the query looks like authorizeRequest().get('xxx').then(xxx).catch(xxx)
Thanks!

Why use promise syntax when you are already using async/await syntax to get your value out of the device storage?
You can rewrite your code using async/await which makes it much easier to see what is going on in your code.
export const authorizeRequest = async (url) => {
try {
const token = await deviceStorage.getItem('accessToken');
const a = await axios.create({
timeout: 2000,
headers: {
'Authorization': 'Bearer ' + token,
'Content-Type': 'application/json'
}
});
const response = a.get(url);
ToastAndroid.show('Response ' + response.data, ToastAndroid.SHORT);
// return response.data // <- you could return something here
} catch (error) {
ToastAndroid.show('error ' + JSON.stringify(error), ToastAndroid.LONG);
}
};
Writing your code in the above way means that you can avoid promise chaining.
You can then use it in the following way:
await authorizeRequest('http://192.168.0.60:8080/users/echo2/asd')
If you want to get a value from the authorizeRequest function you could just return the response.data and you would access it like this:
const data = authorizeRequest('http://192.168.0.60:8080/users/echo2/asd')
Here are some great articles on promises and async/await.
https://medium.com/#bluepnume/learn-about-promises-before-you-start-using-async-await-eb148164a9c8
https://hackernoon.com/6-reasons-why-javascripts-async-await-blows-promises-away-tutorial-c7ec10518dd9

Related

How to properly await Nuxt calls with async/await or .then

Im trying to fetch an API using chaining with .then but I don't figure it out I try like:
async fetch() {
let path = this.$nuxt.context.route.path
this.response = await axios.get(
`/api${path}`,
{
headers: {
'X-AUTH-TOKEN': process.env.SECURE_TOKEN,
'Content-Type': 'application/json'
}
}
).then((res) => {
this.results = res.data.content.slice(0,40);
return results();
})
.then((res) => {
this.results2 = res.data.content.slice(20,40);
return results2();
})
},
For my API data load: when results is finish /results2 start to load, for using it with $fetchState.pending
What will be the best way of doing it? I'm trying to adapt the answer from here but no success so far.
This kind of code should be working fine
<script>
export default {
async fetch() {
this.response = await axios
.get(`/api${this.$route.path}`, { // faster than this.$nuxt.context.route.path
headers: {
'X-AUTH-TOKEN': process.env.SECURE_TOKEN,
'Content-Type': 'application/json',
},
})
.then((res) => { // ❌ this is useless because you're already using await above
const results = res.data.content.slice(0, 40)
return results()
})
.then((res) => { // ❌ this is useless because `slice` is NOT async
const results2 = res.data.content.slice(20, 40)
return results2()
})
},
}
</script>
Otherwise, I can also recommend a better approach overall, using async/await and not mixing it with .then at all, like this
<script>
export default {
async fetch() {
const response = await axios.get(
`/api${this.$route.path}`,
{
headers: {
'X-AUTH-TOKEN': process.env.SECURE_TOKEN,
'Content-Type': 'application/json',
},
}
)
const results = response.data.content.slice(0, 40)
const results2 = results.data.content.slice(20, 40)
},
}
</script>
PS: note that some things are not async, hence do not need await (or .then at all).
It can even be shorten to the following
<script>
export default {
async fetch() {
const response = await this.$axios.$get( // 👈🏻 using the shortcut $get
`/api${this.$route.path}`,
{
headers: {
'X-AUTH-TOKEN': process.env.SECURE_TOKEN,
'Content-Type': 'application/json',
},
}
)
const results = response.content.slice(0, 40) // 👈🏻 no need for `.data` here
const results2 = results.content.slice(20, 40) // 👈🏻 same here
},
}
</script>
Thanks to the shortcuts available with the axios module that you should be using anyway.
As of why you should use async/await, it's just more lisible and available everywhere already (IE is dead). Here is some more info about the whole async/await thing: https://javascript.info/async-await
Far prettier than this kind of syntax (also named callback hell)

axios cancellation caught inside of then() instead of catch()

I making a multi-upload file form.
Upon user cancellation, once the corresponding axios call get cancelled using cancel(), I having a weird behaviour. My axios call get caught inside the then() whereas it should be caught inside of catch(). The response inside of then() returns undefined.
I am having a hard time figuring if I did something wrong on the front-end part, I think my call is may be missing some headers or maybe it's on the backend part ?
const payload = { file, objectId: articleId, contentType: 'article' };
const source = axios.CancelToken.source();
// callback to execute at progression
const onUploadProgress = (event) => {
const percentage = Math.round((100 * event.loaded) / event.total);
this.handleFileUploadProgression(file, {
percentage,
status: 'pending',
cancelSource: source,
});
};
attachmentService
.create(payload, { onUploadProgress, cancelToken: source.token })
.then((response) => {
// cancelation response ends up here with a `undefined` response content
})
.catch((error) => {
console.log(error);
// canceled request do not reads as errors down here
if (axios.isCancel(error)) {
console.log('axios request cancelled', error);
}
});
the service itself is defined below
export const attachmentService = {
create(payload, requestOptions) {
// FormData cannot be decamelized inside an interceptor so it's done before, here.
const formData = new FormData();
Object.entries(payload).forEach(([key, value]) =>
formData.append(decamelize(key), value),
);
return api
.post(resource, formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
...requestOptions,
})
.then((response) => {
console.log(response, 'cancelled request answered here as `undefined`');
return response.data;
})
.catch((error) => {
// not caught here (earlier)
return error.data;
});
},
};
cancellation is called upon a file object doing
file.cancelSource.cancel('Request was cancelled by the user');
As suggested by #estus-flask in a comment, the issue is that I was catching the error inside of the service (too early). Thank you!
export const articleService = {
create(payload, requestOptions) {
// FormData cannot be decamelized inside an interceptor so it's done before, here.
const formData = new FormData();
Object.entries(payload).forEach(([key, value]) =>
formData.append(decamelize(key), value),
);
return api.post(resource, formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
...requestOptions,
});
},
};

How do I use a variable outside a function in react native?

I need to use that variable "let result" outside of this function. I need to use that variable inside of another function. How can I get it?
My code looks like this:
method: "GET",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Cache-Control': 'no-cache',
'Authorization': 'Bearer ' + this.state.clientToken,
},
})
.then((response) => response.json())
.then((responseJson) => {
let result= JSON.parse((responseJson.lstsurveyoncode))
let answer = JSON.parse(responseJson.lstsurveyoncode)[0].qoptions
console.log("responsejson",result[1].QUESTIONCODE)
console.log("answerrr",answer);
console.log("data length",result.length);
this.setState({
isLoading:false,
dataresponse:result,
// count:Object.keys(dataresponse).length
},function(){
});
You can create a global variable, and then update it inside your first function and in the second function you check if the variable is defined
let result;
function firstFunction(){
result = 'Your Json Result'
}
function secondFunction(){
if (!result) return;
// Here you can use your variable result
}
You should read about async-await and state managment in react-native.
For quick solution, you should mark your function with async modifier and await for result. Sample code below:
async function yourRequest() {
return new Promise((resolver, rejected) => {
// your async request
})
.then(response => response.json())
.then(responseJson => {
const result = JSON.parse(responseJson.lstsurveyoncode)
const answer = JSON.parse(responseJson.lstsurveyoncode)[0].qoptions
// ...
return {
result: result,
answer: answer,
}
})
}
async function handleResponse() {
const response = await yourRequest()
// and now, you can access to variables declared at `makeRequest` function
console.log(response.result)
console.log(response.answer)
}

react-native - check expiration of jwt with redux-thunk middleware before every call to API

For my react-native app I need to make sure that before every fetch request to server the use-case below should be executed
-> check the expire date of token that is saved to redux.
--> If token is not expired, app keeps going on with requested fetch to server
--> If token expired, app immediately makes new request to refresh token without making user knows it. After successfully refreshing token, app keeps going on with requested fetch to server
I tried to implement middleware with redux-thunk, but I do not know whether it's good design or not. I just need someone experienced with redux and react to give me feedback over my middleware code.
This is how I make requests to server oveer my app's component through dispatching the checkTokenAndFetch - action creater.
url = "https://———————";
requestOptions = {
method: 'GET',
headers: {
'Authorization': 'Bearer ' + this.props.token
}
};
dispatch(authActions.checkTokenAndFetch(url, requestOptions))
.then((data) => {
})
here is action creator - checkTokenAndFetch located in authActions.js
file where my actions located
function checkTokenAndFetch(url, requestOptions){
return dispatch => {
if(authServices.isTokenExpired()){
console.log("TOKEN EXPIRED");
authServices.refreshToken()
.then(
refreshToken => {
var arr = refreshToken.split('.');
decodedToken = base64.decode(arr[1]);
newTokenExpDate = JSON.parse(decodedToken).exp;
dispatch(writeTokenToRedux(refreshToken,newTokenExpDate));
},
error => {
Alert.alert("TOKEN refresh failed","Login Again");
Actions.login();
}
);
}
else{
console.log("TOKEN IS FRESH");
}
return authServices.fetchForUFS(url, requestOptions)
.then(
response => {
return response;
},
error => {
}
)
;
}
}
Here is isTokenExpired and refreshToken functions that I call for case of token expire, located in another file named authServices.js.
function isTokenExpired(){
var newState = store.getState();
var milliseconds = (new Date).getTime();
var exDate = newState.tokenExpDate;
return milliseconds>exDate*1000
}
function refreshToken(){
var refreshToken = store.getState();
return fetch('https://—————————', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer ' + refreshToken.token
}
})
.then((response) => {
return response._bodyText;
})
.catch((error) => {
return error;
})
}
and my fetchForUFS function in authServices.js to make a call to server after completeing token-check(refresh) stuff.
function fetchForUFS(url,requestOptions){
return fetch(url, requestOptions)
.then((response) => {
return response.json();
})
.then((responseData) =>{
return responseData;
})
.catch((error) => {
})
}
I've read tons of redux-thunk, redux-promise and middleware documentation and I'm yet not sure whether I am implementing middleware logic truly?

Using Axios GET with Authorization Header in React-Native App

I'm trying to use axios for a GET request with an API which requires an Authorization header.
My current code:
const AuthStr = 'Bearer ' + USER_TOKEN;
where USER_TOKEN is the access token needed. This string concatenation may be the issue as if I post this as AuthStr = 'Bearer 41839y750138-391', the following GET request works and returns the data I'm after.
axios.get(URL, { 'headers': { 'Authorization': AuthStr } })
.then((response => {
console.log(response.data);
})
.catch((error) => {
console.log(error);
});
I also tried setting this as a global header with no success.
For anyone else that comes across this post and might find it useful...
There is actually nothing wrong with my code. I made the mistake of requesting client_credentials type access code instead of password access code (#facepalms).
FYI I am using urlencoded post hence the use of querystring..
So for those that may be looking for some example code.. here is my full request
Big thanks to #swapnil for trying to help me debug this.
const data = {
grant_type: USER_GRANT_TYPE,
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
scope: SCOPE_INT,
username: DEMO_EMAIL,
password: DEMO_PASSWORD
};
axios.post(TOKEN_URL, Querystring.stringify(data))
.then(response => {
console.log(response.data);
USER_TOKEN = response.data.access_token;
console.log('userresponse ' + response.data.access_token);
})
.catch((error) => {
console.log('error ' + error);
});
const AuthStr = 'Bearer '.concat(USER_TOKEN);
axios.get(URL, { headers: { Authorization: AuthStr } })
.then(response => {
// If request is good...
console.log(response.data);
})
.catch((error) => {
console.log('error ' + error);
});
Could not get this to work until I put Authorization in single quotes:
axios.get(URL, { headers: { 'Authorization': AuthStr } })