I am trying to query a quote API for a freeCodeCamp project I'm updating to React.js. I am now trying to use Fetch or Axios to query the API but it's caching the response in the browser. I know in $ajax there is a { cache: false } that would force the browser to do a new request.
Is there some way I will be able to do the same with Fetch or Axios?
The cache-control setting seems to be already set to max-age: 0 by Axios.
This is my code I have that is querying the API.
generateQuote = () => {
axios.get('https://quotesondesign.com/wp-json/posts?filter[orderby]=rand&filter[posts_per_page]=1')
.then(response => {
const { title, content, link } = response.data[0];
console.log(title, content, link)
this.setState(() => ({ title, content, link }));
})
.catch(err => {
console.log(`${err} whilst contacting the quote API.`)
})
}
Okay so I found a solution. I had to set a timestamp on the API url to get it to make a new call. There doesn't seem to be a way to force axios or fetch to disable cache.
This is how my code now looks
axios.get(`https://quotesondesign.com/wp-json/posts?filter[orderby]=rand&filter[posts_per_page]=1×tamp=${new Date().getTime()}`)
.then(response => {
const { title, content, link } = response.data[0];
console.log(title, content, link)
this.setState(() => ({ title, content, link }));
})
.catch(err => {
console.log(`${err} whilst contacting the quote API.`)
})
I added these headers to all axios requests and it's working well.
axiosInstance.defaults.headers = {
'Cache-Control': 'no-cache',
'Pragma': 'no-cache',
'Expires': '0',
};
If you do not want to disable caching for all axios requests, you can disable caching for only one request by using the following parameters in the axios call:
axios.get(
'https://YOUR-URL.com',
{
// query URL without using browser cache
headers: {
'Cache-Control': 'no-cache',
'Pragma': 'no-cache',
'Expires': '0',
},
}
)
It seems, adding timestamp is the only always working way.
If you're using Vue, for example:
const api = axios.create({
baseURL: 'https://example.com/api',
params: {
t: new Date().getTime()
}
})
Vue.prototype.$api = api
So you can use it with:
this.$api.get('items')
And it will always add different timestamp to the url, depending on current request time.
I think you just need to make the url different each time you make the axios call. Timestamp is just one way to do so. Also consider disabling or filtering service workers caching method if you are developing a PWA.
Create an instance of axios and then add timestamp to every request.
const axiosInstance = axios.create({})
axiosInstance.interceptors.request.use(
function (config) {
// Do something before request is sent
config.params = { ...config.params, timestamp: Date.now() };
return config;
},
function (error) {
// Do something with request error
return Promise.reject(error);
}
);
Related
I'm building a wine pairing app in Next.js—where when a user clicks on a type of wine (i.e. chardonnay), it calls an API to return the suggested food pairings. This works with no issue when I use NEXT_PUBLIC for my environment variables, but I don't want to expose my private API key.
This is my first time using server-side environment variables with Next.js I understand that this needs to happen within the pages/api folder. API routing with Next.js is still something that I'm learning, so I've been following the docs, and I also found this tutorial which I followed that resulted in the 500 (Internal Server Error). I'm also getting an Axios error. This is a screenshot of both errors—please let me know if anything should be expanded, and I'll post another screenshot.
I also understand that I can use getStaticProps(), but this call is coming from a component rather than a page, and I understand from the docs that getStaticProps() must be called from a page.
This is what my .env.local looks like:
API_KEY=<my api key>
BASE_URL=https://api.spoonacular.com/
This is what my API call looks like (pages/api/wineWithFood.js)
import axios from 'axios';
export default async function wineWithFood(req, res) {
const {
query: { wine },
} = req;
const url = `${process.env.BASE_URL}food/wine/dishes?wine=${wine}&apiKey=${process.env.API_KEY}`;
const response = await axios.get(url);
res.status(200).json({
data: response.data,
});
}
This is the relevant code for what that call looks like in my component which is properly imported into the page where it belongs:
const getPairing = async () => {
axios.get(`/api/wineWithFood?wine=${wine}`, {
headers: {
Accept: 'application/json',
'Access-Control-Allow-Origin': '*',
},
})
.then((response) => response)
.then((response) => {
setData(response.data.pairings)
})
.catch((err) => console.log(err))
}
const handleChange = (e) => {
e.preventDefault();
setWine(e.target.value);
getPairing();
};
console.log(wine)
I see that the request isn't capturing the wine type, but when I console.log the wine, it's showing up in the browser console as expected. When I console.log the response.data from the API call, I get a status code of 400 with a message stating that the wine must not be empty.
Now, if I change my code to the following—I get the same errors and console.logs as I mentioned... but only on the first try! On the second try (clicking the same exact wine), the wine shows correctly in the browser console since I'm console.loging it, but I get that same 500 error in my console, however, now I can see all of the correct data in my terminal! That leads me to believe I'm doing something wrong on the frontend. Here's the tweaked code that results in this:
const getPairing = async (wine) => {
axios
.get(`/api/wineWithFood?wine=${wine}`, {
headers: {
Accept: 'application/json',
'Access-Control-Allow-Origin': '*',
},
})
.then((response) => response)
.then((response) => {
setData(response.data.pairings);
console.log(response.data.pairings);
})
.catch((err) => console.log(err));
};
const handleChange = (e) => {
e.preventDefault();
setWine(e.target.value);
getPairing(wine);
};
I'm happy to check out any other resources to help me out if that's a better answer to this question.
I finally figured this out and wanted to share the answer—which was staring me in the face.
I left my API call in pages/api/wineWithFood.js the same. I was right. The error was on the frontend. I got rid of the getPairing() function and put everything in the handleChange function. When I console logged my response on the frontend, I realized that the info I needed was res.data.data.pairings. I also changed the axios call by using e.target.value as the search query. I removed wine and setWine since it wasn't necessary. Here's the final code:
const handleChange = (e) => {
e.preventDefault();
axios
.get(`/api/wineWithFood?wine=${e.target.value}`, {
headers: {
Accept: 'application/json',
'Access-Control-Allow-Origin': '*',
},
})
.then((res) => {
setData(res.data.data.pairings);
});
};
I hope this can help someone out—also I'm open to feedback if there's a better way.
Here is my code:
const data = {
listing: listing,
};
await axios.post('https://app.jghfjgf.com/m/api/helper/helper', qs.stringify(data) ,{
headers:{
'Cookie':cookie,
'content-type': 'application/x-www-form-urlencoded'
}}
).then(response =>{
console.log(response);
alert(response.data);
}).catch(error => {
console.log(error);
alert(error);
});
I am trying to make HTTP request using Axios library and I tried most of the suggestions available on the web. I don't know what mistake I am doing. I am new to React Native application so don't have enough practice with Axios.
Note: The issue is value "cookie" in header is not passing along with the API so I am facing error.
I'm having a problem with cors. I don't have access to the server, providing the 3rd party API, but it does use the right headers to provide me access. I know, because a native XHR request works, with just putting the authorization and client_id headers, which are required from the api to be set.
I couldn't anyhow make it work with Axios, spent 3 days on this. I'll be really glad if someone helps me out! Please look at the code I made some comments there.
This is the native XHR request, which works:
var data = "{\"birthday\":\"1981-07-07\",\"email\":\"asdiiii#mail.com\",\"phone\":\"1234578901\"}";
var xhr = new XMLHttpRequest();
xhr.addEventListener("readystatechange", function () {
if (this.readyState === this.DONE) {
console.log(this.response);
}
});
xhr.open("POST", "cross-url/api/detail");
xhr.setRequestHeader("authorization", "fake");
xhr.setRequestHeader("client_id", "fake");
xhr.setRequestHeader("content-type", "application/json");
xhr.send(data);
Axios code, which doesn't work:
axios.defaults.headers.common['Accept'] = 'application/json, text/plain'
const instance = axios.create({
baseURL: 'cross-url',
// crossdomain:true, // this doesn't help
//mode:'cors', // this doesn't help too
/*
headers: {
'content-type':'application/json',
'client_id':'client_id_here',
'access-control-allow-origin':'*', // if I put this I get an error it's denied by 'access-control-allow-headers'
'Access-Control-Allow-Headers':
'Accept,Origin,Authorization,client_id,content-type,x-requested-with', // If I put this I get still an error that the header doesn't allow origin'
'Access-Control-Allow-Credentials': 'true',
},
*/
headers: {
'client_id':'fake',
},
transformRequest: [
(data,headers) => {
delete headers.common['X-CSRF-TOKEN']
console.log(data)
// return JSON.stringify(data) // this also doesn't work'
return data
},
],
});
instance.defaults.headers.common['authorization'] = 'fake';
const postData3 = {
email:'fake',
phone:'123123123',
birthday:'1981-07-07',
}
instance.post('/api/detail', postData3).then((response) => {
console.log(response)
}).catch((e) => {
console.log(e)
console.log(e.request)
})
The server determines what headers are allowed, what methods are allowed, and what hosts are allowed.
access-control-allow-xxx are a server-to-client headers, and for all practical purposes, no servers will accept them.
Concerning CORS
Remove your access-control.xxx headers and then look at the response. If denied, the server will let you know why.
If you do not have access to the server, and your host, method, and/or client-headers are denied, all you can do is use a proxy (forward your calls from the browser to an intermediate server). You will need access to some server for that however.
I'm working with Vue to interact with an external API on a Drupal website, but in order to do so dynamically per Drupal user, I need to get a token from Drupal first. To do so, I'm trying to do two GET requests. The first gets me a bearer token out of Drupal, and the second uses it to authenticate the third-party API request.
Below is what I'm trying – I'm able to get the token successfully in the first request's response, but not when I try to use it in the header of my second request. If I try hardcoding the token that I see in the console log, it does work, so I know none of that is the issue. It's just that this.jwt['data']['token'] in the second request's headers seems to not pull back the token.
What do I need to adjust in order to access the token from the first response as part of the headers of my second request?
created() {
axios
.get('/jwt/token')
.then(response => {
this.jwt = response
console.log(this.jwt['data']['token']) // this does show what I want to access later
})
},
mounted() {
axios
.get('/comment/doc/' + this.id, {
headers: { Authorization: "Bearer " + this.jwt['data']['token'] } // ...but this doesn't work
})
.then(response => {
this.comments = response
})
},
It's likely the response to the token request has not finished by the time the component mounts, at which point this.jwt is not yet assigned.
I would move the token request into the mounted hook, fetching comments only when the token request succeeds:
export default {
mounted() {
axios
.get('/jwt/token')
.then(tokenResp => {
this.jwt = tokenResp
axios
.get('/comment/doc/' + this.id, {
headers: { Authorization: 'Bearer ' + this.jwt['data']['token'] }
})
.then(commentsResp => {
this.comments = commentsResp
})
})
}
}
I'm facing an issue while using react native fetch api. many times request got failure . I have a high speed connection. but many times it got failed.
that issue is happening In android,ios both.
const shoppingApi = 'myserverlink';
async function Sendshoppinapi(data) {
try {
let response = await fetch(shoppingApi, {
method: 'POST',
headers: {
'Accept': 'application/json',
'content-type':'multipart/form-data'
},
body: data
});
let responseJson = await response.json();
return responseJson;
}
catch (error) {
Alert.alert(error.toString())
}
}
export {Sendshoppinapi};
data that I sending server as post request
add_to_wishlist = (item,index) => {
{
let data = new FormData();
data.append('methodName', 'add_to_wishlist');
data.append('user_id', global.userid)
data.append('item_id', this.props.navigation.state.params.itemid.toString())
Sendshoppinapi(data).then((responseJson)=>{
console.warn(responseJson);
if(responseJson.responseCode == '200'){
this.setState({fav:false})
Alert.alert('SHOPPING','Item added to wishlist successfully.',[{text: 'OK',},],{ cancelable: false })
}
else{
this.setState({fav:false})
Alert.alert('SHOPPING','Item already .',[{text: 'OK',},],{ cancelable: false })
}
})}
}
Error that when request got failed
I've quoted an answer I used for another post - however I have added await.
You can check the status of the call, to determine perhaps why the network call failed. Try using fetch's ok to check whether the response was valid, for example:
.then(function(response) {
if (!response.ok) {
//throw error
} else {
//valid response
}
})
Using await:
let response = await fetch(url)
if (response.ok) return await response.json()
You can also access the response's status like:
response.status;
or also, statusText such as:
response.statusText;
checkout the below:
https://developer.mozilla.org/en-US/docs/Web/API/Response/statusText
https://developer.mozilla.org/en-US/docs/Web/API/Response/status
https://www.tjvantoll.com/2015/09/13/fetch-and-errors/
Use then() function with promises. (Requested code snippet)
fetch(shoppingApi, {
method: 'POST',
headers: {
'Accept': 'application/json',
'content-type':'multipart/form-data'
},
body: data
})
.then((resp) => {
return resp.json()
})
.then((resp) => {
//resp contains your json data
});
You also can make your function returns a Promise, and use it with then():
function sendShoppingApi(data) {
return new Promise((resolve, reject) => {
fetch(shoppingApi, {
method: 'POST',
headers: {
'Accept': 'application/json',
'content-type':'multipart/form-data'
},
body: data
})
.then((resp) => {
return resp.json();
})
.then((resp) => {
resolve(resp);
/*
you should also check if data is valid, if something went wrong
you can reject the promise:
if(!dataOK)
reject("error message");
*/
});
});
}
So now you can do something like this:
sendShoppingApi(data)
.then((resp) => {
//do stuff with your data
})
.catch((err) => {
//handle error
});
UPDATE
could be a duplicate of this: React Native fetch() Network Request Failed
For the case when you are running the app on the android device, the API is on a computer and both of them are on the same network I have added some possible things to check. I haven't detailed specific solutions since there are many answers on each topic.
Do a quick check with ngrok https://ngrok.com/ on the free plan to see if that works. If yes:
Make sure the API is accessible by trying to access it on the device browser (most important is to check if you allow the port at inbound rules, firewall).
If you are using HTTPS, you might get an error if your react native env is not properly configured to accept not trusted certificates, assuming you are using a non trusted one. Do a check without HTTPS, only with HTTP, to see if it's the case. https://github.com/facebook/react-native/issues/20488