How to set token into onError ApolloClient? - react-native

I want to handle error if return error will call setContext for query again but I can't get token in local state because onError does not accept async. How I can do that.
Thanks
//
export default async () =>
new ApolloClient({
cache: new InMemoryCache({
dataIdFromObject: object => object.key || null
}),
uri: Api.GRAPH_QL_URL,
clientState: { defaults, resolvers },
// cho handle network failed
///onError not accept async
onError: ({ graphQLErrors, networkError, operation, forward }) => {
// how to get token in hear
if (networkError) {
operation.setContext({
headers: {
Accept: "application/json",
authorization: token !== null ? `JWT ${token}` : "" // how to put token into authorization
}
});
}
},
// Call query
request: async operation => {
const token = await AsyncStorage.getItem(strings.keyToken);
console.log("Client request: ", {
operationName: operation.operationName,
variables: operation.variables,
query: operation.query,
jwtoken: token
});
operation.setContext({
headers: {
Accept: "application/json",
authorization: token !== null ? `JWT ${token}` : ""
}
});
},
});
//I want to handle error if return error will call setContext for query again but I can't get token in local state. How I can do that.
Thanks

You need to set an Observable in order to work with async/await inside the onError method.
Import it from apollo-link (import { Observable } from 'apollo-link';).
onError: ({ graphQLErrors, networkError, operation, forward }) => {
if (networkError) {
return new Observable(async (observer) => {
const token = await AsyncStorage.getItem(strings.keyToken);
const headers = operation.getContext().headers;
operation.setContext({
headers: {
...headers,
Authorization: token !== null ? `JWT ${token}` : ''
}
});
const subscriber = {
next: observer.next.bind(observer),
error: observer.error.bind(observer),
complete: observer.complete.bind(observer),
};
return forward(operation).subscribe(subscriber);
});
}
}

Related

how can i add headers in vue js using async/await

i'm trying to send a request to the backend which uses headers, please how can i add the headers
this is my script tag
<script>
import axios from "axios";
export default {
data: () => ({
fullName: "",
streetAddress1: ""
}),
created() {
//user is not authorized
if (localStorage.getItem("token") === null) {
this.$router.push("/login");
}
},
methods: {
async onAddAddress() {
const token = localStorage.getItem("token");
headers: {
"Content-Type": "application/json",
Authorization: "Bearer" + token,
"x-access-token": token
}
try {
let data = {
fullName: this.fullName,
streetAddress: this.streetAddress1
};
const response = axios
.post("http://localhost:5000/api/addresses", data)
.then(res => {
console.log(res);
});
console.log(response);
} catch (error) {
console.error("error >>", error);
}
}
}
}
this code gives me an error, please how can i go about this
There are a few problems with your code. For instance you do not define headers as a variable and you do not add it to your axios request as a third argument. I think you need something like this:
async onAddAddress() {
const token = localStorage.getItem("token");
/// define headers variable
const headers = {
"Content-Type": "application/json",
Authorization: "Bearer" + token,
"x-access-token": token
};
let data = {
fullName: this.fullName,
streetAddress: this.streetAddress1
};
try {
/// config as the third argument.
conts result = await axios.post("http://localhost:5000/api/addresses", data, { headers });
console.log(result);
}
catch (error) {
console.error("error >>", error)
}
}
For async/await to work, you need to add await in front of the axios call.
Hope this helps.

Using Axios GET with Authorization Header in vue App

I'm trying to use axios for a GET request with an API which requires an Authorization header.
here is my current code
My current code:
data () {
return {
listings: [],
AuthStr : 'Bearer ' + JSON.parse(localStorage.getItem('token')),
}
},
created () {
axios.get(`url`, { 'headers': { 'Authorization': AuthStr } })
.then((response => {
this.listings = response.data;
})
.catch((error) => {
console.log(error)
})
}
it shows me 403 error I don't know why.
There are several ways to to add header to request.
For a single request:
let config = {
headers: {
Authorization: value,
}
}
axios.get(URL, config).then(...)
you need to call data().AuthStr to get your token there is a typo.
Your created function will be
created () {
axios.get(`url`, { 'headers': { 'Authorization': data().AuthStr } })
.then((response) => {
this.listings = response.data;
})
.catch((error) => {
console.log(error)
})
}
It should be:
axios.get(`url`, { 'headers': { 'Authorization': this.AuthStr } })
You are using JSON.parse when getting the value for AuthStr. JSON.parse returns an object. Try removing it and if you are using the correct Auth token, it should work.

AsyncStorage data changing upon app restart

I'm currently calling a JSON api to set an auth token which I'll just be storing in the AsyncStorage to persist between app life so a user doesn't have to log in every single time.
I'm currently setting that token like so:
fetch(url, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(this.state)
})
.then( resp => {
return resp.json();
})
.then( async (data) => {
if ('error' in data) {
this.setState({
error: data.error,
password: ''
})
this.secondTextInput.focus();
}
if ('access_token' in data) {
try {
await AsyncStorage.setItem('access_token', data.access_token);
} catch (error) {
return error;
}
this.props.navigation.navigate('Main');
}
})
.catch(
error => {
console.error(error)
return error;
}
);
If I then call AsyncStorage.getItem('access_token') After killing the app or reloading it I'm winding up with this output:
{
"_40":0,
"_65":0,
"_55":null,
"_72":null
}
If I then call AsyncStorage.getItem('access_token') Before killing the app or reloading it I'm winding up with the correct access token. I've double checked the code and I'm not using AsyncStorage.setItem('access_token') anywhere else.
This is how I'm retrieving my token:
componentDidMount() {
console.warn('Mounting');
try {
let token = AsyncStorage.getItem('access_token');
console.warn(token);
if(token !== null) {
console.error(token);
}
} catch (error) {}
AsyncStorage.getItem() is a asynchronous action just like setItem(), so you need to wait until the Promise has been resolved before logging.
Edit
Tip: if you see some strange output like that it is always related to a Promise which is not yet resolved or rejected
I've solved my issue by using #dentemm's recommendation of creating an async function.
async _getToken() {
try {
var token = await AsyncStorage.getItem('access_token');
return token;
} catch(e) {
console.error(e);
}
}
componentDidMount() {
let token = null;
this._getToken()
.then( rsp => {
fetch(global.url + '/api/auth/refresh', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + rsp
}
})
.then(rsp => {
return rsp.json();
})
.then(data => {
if('access_token' in data) {
try {
AsyncStorage.setItem('access_token', data.access_token);
} catch (error) {
return error;
}
this.props.navigation.navigate('Main');
}
})
.catch( error => {
return error;
})
});
}
This way I can get my token from the storage then run my refresh function to get an updated token to use for future requests.

Axios interceptors - Not using instance until AsyncStorage resolved?

I've an Axios Interceptor setup to manage responses and cut down on re-writing code everywhere. Currently, I need to add the Authorization header using the { config } object in each call like below.
apiCall = () => {
const token = await AsyncStorage.getItem('JWT_BEARER_TOKEN');
const config = {
headers: {
'Authorization': 'Bearer ' + token,
}
}
const attendance = await axiosInstance.post('/team/matchday', data, config);
// ... do something with attendance
}
I'd like to do it in the axiosInstance I've create as below, but I'm getting a promise rejected error. I presume this is because token is still an incomplete promise when it is returned.
Any ideas how to handle this config correctly?
import { AsyncStorage, Alert } from 'react-native';
import axios from 'axios';
const ReturnAxiosInstance = async (token) => {
const AxiosInstance = axios.create({
baseURL: 'http://localhost:4000',
timeout: 3000,
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + await AsyncStorage.getItem('JWT_BEARER_TOKEN'),
},
});
AxiosInstance.interceptors.response.use(
response => response,
(error) => {
if (!error.response) {
Alert.alert('Network Error!');
return console.log(error);
// return dispatch({ type: 'NETWORK_FAILURE' });
} else if (error.response.status === 500) {
Alert.alert('Server Error!');
} else if (error.response.status === 404) {
Alert.alert('Endpoint doesn\'t exist!');
}
// handle the errors due to the status code here
return error.response;
},
);
return AxiosInstance;
};
export default ReturnAxiosInstance();
You need to add in the request interceptor for your Axios instance.
// ...
axiosInstance.interceptors.request.use(
async (config) => {
config.headers.authorization = await AsyncStorage.getItem('JWT_BEARER_TOKEN');
return config;
},
error => Promise.reject(error)
);
// ...

Cannot get correct error from Axios

I have a doFetch function that handles all my api calls:
const doFetch = function(params){
...
// Make request using Axios. Axios is promise based.
return axios({
method: method,
url: baseUrl + url,
data: queryParams,
timeout: timeout,
headers: {
'Content-Type': contentType,
'Authorization': `bearer ${Auth.getToken()}` // set the authorization HTTP header
},
responseType: responseType
}).then((response) => {
if(typeof params.callback === "function"){
params.callback(response);
}
else {
return response;
}
}).catch((err) => {
if(typeof params.error === "function") {
if (err.response) {
params.error(err.response.data);
}
}
else{
if (err.response) {
return err.response.data;
}
else{
return err;
}
}
});
};
One such api call is returning a custom error like so (express server):
return res.status(400).json("There was an error on the server.");
The function that calls doFetch is saveUser:
saveUser(userObj).then((response) => {
console.log("No Error");
}).catch((error) => {
console.log("Error:", error);
});
The problem is that I am seeing No Error in the terminal, when I should only be expecting the error message to show. Any ideas?
I like to return promise exactly, to be sure that it does/returns what I want.
I don't like to rely on "promise"-s of 3rd parties.
So I would recommend You to wrap it inside of promise and resolve/reject responses/errors manually:
const doFetch = params => {
...
// Make request using Axios. Axios is promise based.
return new Promise((resolve, reject) => {
axios({
method: method,
url: baseUrl + url,
data: queryParams,
timeout: timeout,
headers: {
'Content-Type': contentType,
'Authorization': `Bearer ${Auth.getToken()}` // set the authorization HTTP header
},
responseType: responseType
})
.then((response) => {
console.info('doFetch:', response); // for debug purposes
if(typeof params.callback === "function"){
params.callback(response);
}
resolve(response);
})
.catch((err) => {
console.error('doFetch:', err); // for debug purposes
const error = (err.response) ? err.response.data : err;
if(typeof params.error === "function") {
params.error(error);
}
reject(error);
});
};
};