React.js: Accessing the Retry-After header in an API response - reactjs

I'm a newer programmer using React.js and the Spotify API for a music app. I’m trying to access the Retry-After header in a 429 rate-limiting error response (Spotify docs here). This is my code currently, which I loosely copied from this article.
async getArtistArt (artistID) {
let url = `https://api.spotify.com/v1/artists?ids=${artistID}`
let response = await fetch(url, {
headers: {
'Authorization': 'Bearer ' + localStorage.getItem('accessToken')
},
});
let data = await response.json();
if (Object.keys(data)[0] === "error" && data.error.status === 429) { // Handle rate limiting
console.log('Error!');
console.log(data);
for (var pair of data.headers.entries()) {
console.log(pair);
console.log(pair[0]);
}
}
return data;
}
This is what I see in the console:
screenshot here
console.log('Error!'); // Logs 'Error!'
console.log(data); // Logs error object, but not the header
console.log(pair) // Error that says 'Uncaught (in promise) TypeError: Cannot read property 'entries' of undefined'
I've tried not putting the response into json but that seemed to have no effect.
I've tried to avoid try/catch error statements as I’ve heard they’re somewhat outdated and not usually recommended, but would I need them to access the response header?
I would be grateful for any advice. A big thank you in advance!

I think I found where your error is. The code is almost correct except that you are checking for the headers in the json of the response.
Try to do for (var pair of response.headers.entries()) {...}, so that you are taking the response rather than its json content.

Related

react-jsonp giving a CORB error in a React App

Edit
based on suggestions tried doing it with fetch, now I am getting this
I am trying to get data from gnews.io/api in a simple react app.I am learning React and the solution might have been to easy but I am stuck here and cant figure out what is wrong and I keep getting this error
fetch-jsonp.js:88 Cross-Origin Read Blocking (CORB) blocked cross-origin response https://gnews.io/api/v4/top-headlines?country=us&token=myapi with MIME type application/json.
funny thing is that if I copy this url https://gnews.io/api/v4/top-headlines?country=us&token=MYAPI&callback=jsonp_1642257010280_37644
and paste it in the browser I am getting the desired response.
Would really appreciate any sort of help
this is the useEffect function that is making this api call
React.useEffect(()=> {
async function getNewsdata(country){
try {
let url = `https://www.gnews.io/api/v4/top-headlines?country=pk&token=${API2}`
let response = await fetchJsonp(url)
let result = await response.json()
console.log("News Data:", result)
} catch (error) {
console.log("error loading news data: ", error)
}
}
getNewsdata(props.country.trim())
},[])
Resorting to JSONP is frowned upon nowadays; there are safer alternatives, such as CORS, for cross-origin communication. As the response's content type is application/json, using JSONP will not work anyway because it causes Chrome's CORB feature to kick in.
Why not try to solve whatever CORS issue you seem to be having? I'd be very surprised if the API you're using weren't configured for CORS... A casual inspection of their documentation reveals that you're using the wrong domain, www.gnews.io, instead of gnews.io. The former redirects to the latter, but is not configured for CORS, which explains your CORS troubles.
Once you use the right domain (gnews.io), all your CORS troubles go away. And because there's no longer any need to reach for dirty tricks like JSONP, you can use good old reliable fetch rather than some third-party tool.
React.useEffect(()=> {
async function getNewsdata(country){
try {
let url = `https://gnews.io/api/v4/top-headlines?country=pk&token=${API2}` // gnews.io, not www.gnews.io
let response = await fetch(url) // fetch, not fetchJsonp
let result = await response.json()
console.log("News Data:", result)
} catch (error) {
console.log("error loading news data: ", error)
}
}
getNewsdata(props.country.trim())
},[])

Bad Request uploading file to hapi

I have followed many tutorials and done many combination but I still cannot get hapi to get the payload properly and I always get a 400 bad request reply.
This is the code in the frontend:
public uploadFile = (file: File) => {
console.log(file.name); /// <--- Correctly displays the name, so the files seems to be loaded correctly
const url = "/api/upload_resource";
const formData = new FormData();
formData.append('file', file);
formData.append('something', "else");
fetch(url, {
method: 'POST',
headers:{"Content-Type": "multipart/form-data"},
body: formData
})
.then((response: any) => { console.log(response);/* Done. Inform the user */ })
.catch((e) => { /* Error. Inform the user */ })
}
And this is the entry in server.route
{ path: "/api/upload_resource", method: "POST",
options:{handler: (e,h)=>{return h.response({}).code(200);},
payload:{ maxBytes: 30485760, parse: true, output:'file',
allow: ['multipart/form-data', "application/JSON"]
}
}
},
I'm using hapi 19.x
Solved,
TLDR:
remove the header in the fetch call and add another key multipart: {output: "file"} in options.payload in the server route item
Now this is how I figured out:
I managed to add a failAction method to the payload to be able to get a more verbose error
payload:{failAction: async (r:any, h:any, e:any) => {console.log(e.message);}, maxByt...
the failAction reported the following error
Invalid content-type header: multipart missing boundary
Ok, back to google to check what the heck a boundary is. After somehow managing to add a boundary, I got a new error message, clearly indicating progress
Unsupported Media Type
I was already familiar with that error message. And, according to many answers in other post, the solution seemed to be adding "Content-Type": "multipart/form-data" to the header. But that led to the "Bad Request Message" issue, and the posts referencing this error suggested to remove it.
So I knew the problem was the "Unsupported Media Type" error message and that the solution should be in the backend not the frontend. So I just searched "Unsupported Media Type hapi" and discovered the missing one payload option in the route.

Book info Goodreads API

Trying to do a search on the Goodreads API for book information. At the same time I'm trying to convert the data from xml to json. I get this error when I do a search for a book
No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access.
and warning
Cross-Origin Read Blocking (CORB) blocked cross-origin response https://www.goodreads.com/search/index.xml?key=PL6saHk8cIrLeeLG3eylg&q=halo with MIME type application/xml. See https://www.chromestatus.com/feature/5629709824032768 for more details.
I installed xml2js package and placed it into my function like this
searchBooks = async (e) => {
e.preventDefault();
const search = this.state.search;
try {
let res = await axios.get(
`https://www.goodreads.com/search/index.xml?key=PL6saHk8cIrLeeLG3eylg&q=${search}`
);
let xml = res.data;
parseString(xml, (error, res) => {
this.setState({
books: res.data
});
});
} catch (error) {
this.setState({ error });
}
console.log(this.state.books);
};
What do I need to fix?
GoodReads API doesn't let you make an API call from front-end. (Refer to this GoodReads forum question).
You need to make an API request from back-end service (e.g. nodejs server), or go through a proxy.
Setting up a proxy can be a pain, so you can call the API using YQL (Yahoo's Query Language) client.
So here is the workaround.
⚠️ WARNING: This is not a good practice but writing it here for an academic purpose.
Please consider setting up your own back-end service to call the API.
Calling YQL directly can be hairy, so you can use another library called, proxyfy-url, which gives you a proxified YQL URL.
var proxify = require('proxify-url');
...
get proxyfiedUrl() {
// GoodReads API returns result in "XML" format.
// "XML" is the "input" format fed into YQL
let proxyUrl = proxify(this.url, { inputFormat: 'xml' });
return proxyUrl;
}
Shameless plug!
If you want more information, I wrote about the same problem on my blog
How to call GoodReads API using YQL

How to handle api errors using aws-amplify?

I'm currently trying to POST data to my aws lambda functions triggered by aws api-gateway using the aws-amplify react lib.
Here is the code :
API.post("snippets","snippets/", {
body: data,
}).then(response => response).catch(console.log(err))
In the main case, everything is OK.
But my lambda function is design to validate the input data and return a status code 400 with a returned payload looking like that :
{
"errors": [
{
"field": "title",
"message": "This field is required"
}
]
}
I would like to catch those errors in order to display them in the frontend but aws-amplify seems to have an undocumented behavior.
By default, status code 400 returned are throw with a default error message :
Error: Request failed with status code 400
at createError (createError.js:16)
at settle (settle.js:18)
at XMLHttpRequest.handleLoad (xhr.js:77)
Is there a way to get the returned payload instead of this magical error?
It turns out that under the hood, aws-amplifyuse Axios to make http calls.
When using Axios, you have to console.log(error.response): https://github.com/axios/axios/issues/960
Here is the fix I've made :
API.post("snippets","snippets/", {
body: data,
}).then(response => response).catch(error => console.log(error.response.data))
A Pull Request on the aws-amplify documentation is open : https://github.com/aws/aws-amplify/pull/633
I also faced the similar issues, It showed the default error message "Request failed with status code 400", instead of the message that is returned from API.
I logged the Error object and it did not show the response attribute in it. But we do have response attribute. I tried logging the Error.response and it did contain the response sent from the API.
Just figured out this by going through the 'Cancel API requests' Amplify docs.
From what I can see this is the contents of the error object returned by the API call:
Heres what I am doing to just print out the error, obviously you would do a lot more here but its a good start.
async uploadUser(state, payload) {
const promise = API.graphql({
query: createUser,
variables: { input: payload },
});
try {
await promise;
} catch (error) {
// Print out the actual error given back to us.
console.log(error.errors[0].message);
// If the error is because the request was cancelled we can confirm here.
if (API.isCancel(error)) {
// handle user cancellation logic.
console.log(error.message);
}
}
Hope that helps 😃

Unexpected token u in JSON at position 0

when I'm try to convert string to object I get error ``:
Unexpected token u in JSON at position 0
Service
setUser : function(aUser){
//sauvegarder User
localStorage.setItem('User', JSON.stringify(aUser));
},
getUser : function(){
//récuperer User
return JSON.parse(localStorage.getItem('User'));
}
The first thing to do is to look at what you're trying to parse. My guess is that you'll find it's "undefined", which is invalid JSON. You're getting undefined because you haven't (yet) saved anything to that key in local storage. undefined is then converted to the string "undefined" which JSON.parse can't parse.
I usually store and retrieve things in local storage like this:
Storing (just like yours):
localStorage.setItem("key", JSON.stringify(thing));
Retrieving (this is different):
thing = JSON.parse(localStorage.getItem("key") || "null");
if (!thing) {
// There wasn't one, do whatever is appropriate
}
That way, I'm always parsing something valid.
You are getting this error because you are not returning the response as a JSON string while your browser is expecting a JSON string to parse. Hence it is taking the first letter of your response string and throwing an error.
You can verify it by going to the networking tab of your browser's Dev tools and viewing the response.
To resolve this issue, you can use the code below in your http request.
var promiz = $http.post(url, data, {
transformRequest: angular.identity,
transformResponse: angular.identity,
headers: {
'Content-Type': undefined
}
});
Hope this helps!
I was also getting the same error. The problem was the response I was getting from HTTP Get Request was not in JSON format, Instead it was plain text.
this.BASE_URL = "my URL";
public getDocument() {
return this.http.get(this.BASE_URL)
.map((res: Response) => res.json())
.catch((error: any) => Observable.throw(error.json().error || 'Server error'));
}
So, the JSON Parser was throwing an error.
When I map it into plain text like this:
.map((res: Response) => res.text());
It works.

Resources