fetch fails to load resource when receiving 404 - javascript

I got a method that fetches data from an API, but it logs errors when receiving an error response (404, 500).
I would just like to return 'undefined' as data if the status is an error, what's my mistake here, how do I get rid of the errors on my console?
fetch.js
export default (url, method, authorization, body) => {
const headers = { 'Content-Type': 'application/json' }
if (authorization) headers.Authorization = localStorage.getItem('id_token');
return fetch(url, {
method,
headers,
body: JSON.stringify(body)
}).then(res => res.ok ? res.json() : undefined)
.catch(console.log);
}
console
sources

I think Chrome always shows an error in the console when any request returns a HTTP error code in the 4XX or 5XX range, there's not much you can do about that.
The reason your catch isn't triggering is because when you return something in a then (or a catch) then that puts the promise chain back into a success mode. If you want to put it into a failure mode, and trigger the next catch, then you need to return specifically a rejected Promise:
Try this instead:
export default (url, method, authorization, body) => {
const headers = { 'Content-Type': 'application/json' }
if (authorization) headers.Authorization = localStorage.getItem('id_token')
return fetch(url, {
method,
headers,
body: JSON.stringify(body),
})
.then(res => {
res.ok ? res.json() : Promise.reject(undefined)
})
.catch(console.log)
}

Every time that you add the .catch() , the code go there if there is an error and skip the (res) => part.
So if you want to get rid of the errors of your console you can:
export default (url, method, authorization, body) => {
const headers = { 'Content-Type': 'application/json' }
if (authorization) headers.Authorization = localStorage.getItem('id_token');
return fetch(url, {
method,
headers,
body: JSON.stringify(body)
}).then(res => res.ok ? res.json(console.log(res)) : undefined) // check the result directly
}

Related

Expo json Unexpected End of Input

I have a pretty simple bit of javascript that sends a post command. As expected, I get a promise from the fetch command, but when trying to use the promise with response.json() I get an Unexpected end of input Syntax Error at the line indicated below. I get no issue when using response.text() and various other methods, it just seems to be .json() that breaks stuff. I am using React Native and this same code has worked fine with Node.js.
async function postData (url, data) {
const params = {
mode: 'no-cors',
method: 'POST',
headers:{
Accept: 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
};
let response = await fetch(url,params);
var responseJson = await response.json();//This seems to be the problem
return response;
}
Here is some more code that may be helpful.
function postStuff() {
var url = "http://192.4.20.69:1337";
postData(url, data)
.then(res => {
console.log(res);
});
}
export default function App() {
console.log("App Executed");
return (
<View style={styles.container}>
<StatusBar style="auto" />
<Text>Hello!!</Text>
<Button
title="Post"
color={"#696969"}
accessibilityLabel="Click to send message"
onPress={postStuff}
/>
</View>
);
}
Any help figuring out why .json() is breaking this would be appreciated. I'd like to try sticking with using fetch but if I need to I can use one of the other ways of handling json but I think I may still run into the issue concerning .json().
Probably the issue lies in the server side response.
response.json() fail if no json is returned.
I suggest to you to check server response by adding a try catch around response.json() and print out response.text() in the catch .
It could also be due to the fact that your response is missing the required header content-type and fetch doesn't know how to parse it.
So you could try yo do something like this
async function postData (url, data) {
const params = {
mode: 'no-cors',
method: 'POST',
headers:{
Accept: 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
};
let response = await fetch(url,params);
try{
return await response.json();//This seems to be the problem
}catch(e){
console.error(e);
const textResponse = await response.text();
return JSON.parse(textResponse)
}
return response;
}
Also I notice that you are returning response instead of responseJson that also could be the problem here

weird CORS problem caused by custom config for fetch

I was writing a little wrapper for fetch method in JavaScript (I am very aware of libs like Axios that can do the same thing). I got the idea from a blog post
My code looks like this
async function apiCall(
endpoint,
{ data, headers: customHeaders, ...customConfig } = {}
) {
console.log("endpoint", endpoint);
const config = {
method: data ? "POST" : "GET",
body: data ? JSON.stringify(data) : undefined,
headers: {
"content-type": data ? "application/json" : undefined,
...customHeaders
},
...customConfig
};
return window.fetch(endpoint, config).then(async (response) => {
if (response.ok) {
return response.json();
} else {
// By default, window.fetch will only reject a promise if the actual request itself failed (network error), not if it returned a "Client error response".
const error = await response
.json()
.catch(() => new Error("invalid json"));
return Promise.reject(error);
}
});
}
export function requestMovies(query) {
const endpoint = `${apiULR}?apikey=${API_KEY}&s=${encodeURIComponent(query)}`;
return apiCall(endpoint);
}
However, I encountered TypeError Failed to fetch which I believed is caused by CORS.
If I take out config from window.fetch as in
async function apiCall(
endpoint,
{ data, headers: customHeaders, ...customConfig } = {}
) {
return window.fetch(endpoint).then(async (response) => {
if (response.ok) {
return response.json();
} else {
// By default, window.fetch will only reject a promise if the actual request itself failed (network error), not if it returned a "Client error response".
const error = await response
.json()
.catch(() => new Error("invalid json"));
return Promise.reject(error);
}
});
}
The problem would be gone. Not sure which part exactly triggered this CORS problem...
Here is a live demo: https://codesandbox.io/s/charming-saha-4c2bh?file=/src/index.js
follow the data not given path:
the ternary goes into the false case
headers gets an entry content-type: undefined
the request gets this header added
request is rejected by api because it contains a content-type header (probably with the string 'undefined' in it)
Solution: Dont use a ternary here, and replace it with an if, to get rid of the undefined entry.
Also: read up on differences between null, undefined values and "has own property" in javascript objects

ReactJS asynchronous global API request method, how to handle response?

I am attempting to build a global API request function considering the repetitiveness of it. The problem I have is despite the responseBody object not being null when the function ends, the response seems to be null?
I can only assume this is in part due to the object being returned before it is updated.
Here is the function:
export function restRequest(url, method, content, body) {
fetch(API_BASE_URL + url, {
method: method,
headers: new Headers({
'Content-Type': content,
'Access-Control-Request-Method': method,
// 'Authorization': localStorage.getItem(ACCESS_TOKEN)
}),
body: body
}).then(
function (response) {
response.json().then((data) => {
let json = JSON.parse(JSON.stringify(data));
let responseBody = {
code: response.status,
body: json
};
//at this point the responseBody is not null
return responseBody;
});
}
)
.catch(function (err) {
console.log('Fetch Error :-S', err);
});
However if I make a call:
let response = restRequest('/app/rest/request', 'GET', 'application/json;charset=UTF-8', null);
response is always null.
What is the best way to handle this?
It's asynchronous, so any call of restRequest will not immediately return the responseBody = you need to properly chain the promises, and call .then on the restRequest call. Return the fetch call from the restRequest function, and avoid the promise-callback antipattern by returning response.json() immediately, rather than nesting a .then inside it:
export const restRequest = (url, method, content, body) => (
fetch(API_BASE_URL + url, {
method: method,
headers: new Headers({
'Content-Type': content,
'Access-Control-Request-Method': method,
// 'Authorization': localStorage.getItem(ACCESS_TOKEN)
}),
body
})
.then(response => Promise.all([response.status, response.json()])
.then(([code, body]) => ({ code, body }))
.catch(function(err) {
console.log('Fetch Error :-S', err);
})
);
then do
restRequest('/app/rest/request', 'GET', 'application/json;charset=UTF-8', null)
.then(response => {
// do stuff with response. (if there was an error, response will be undefined)
});

Unhandled Rejection Type Error from API fetch

I got a weird error while working on my Spotify Web Application. I try to save a playlist to my account, which has to fetch the id element from the https://api.spotify.com/v1/me endpoint, then apply the playlist to your account. Everything seems fine otherwise, besided the fetch to that endpoint, which throws the error:
Spotify.js:81 Uncaught (in promise) TypeError: Failed to execute 'fetch' on 'Window': The provided value is not of type '(sequence<sequence<ByteString>> or record<ByteString, ByteString>)'
I've never seen this error before, and am not sure why it's happening. The findUserId method is:
findUserId() {
if(accessToken === undefined) {
this.getAccessToken();
}
console.log(accessToken);
let userId;
fetch(`https://api.spotify.com/v1/me`, {headers: `Bearer ${accessToken}`}
).then(response => {return response.json()}
).then(jsonResponse => {
userId = jsonResponse.id;
});
console.log(userId);
return userId;
}
First, you have to set the Authentication header inside headers. Also, fetch is async, which means that you try to log userId before the network request has finished. To fix that, put your log inside the second then callback and return the fetch:
findUserId() {
if (accessToken === undefined) {
this.getAccessToken();
}
console.log(accessToken);
return fetch(`https://api.spotify.com/v1/me`, {
headers: { Authentication: `Bearer ${accessToken}` }
})
.then(response => response.json())
.then(jsonResponse => {
userId = jsonResponse.id;
console.log(userId);
return userId;
});
}
Then you can use findUserId like this:
async otherFunction() {
const userId = await this.findUserId();
console.log(userId);
}
or like this:
otherFunction() {
this.findUserId().then(userId => console.log(userId));
}
headers should be an object - change
{headers: `Bearer ${accessToken}`}
to
{headers: {'Authorization': `Bearer ${accessToken}`}}
That's not how you do headers with fetch. I think you mean to set the authorization header. See https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Headers
edit: wrong link

catching error body using axios post

I am sending a status code 422 from my backend code with response body which contains the description of the error. I am using axios post as below to post a request:
post: function(url, reqBody) {
const request = axios({
baseURL: config.apiUrl,
url: url,
headers: {
'Content-Type': 'application/json',
'Authorization': sessionStorage.getItem('token')
},
method: 'POST',
data: reqBody,
responseType: 'json'
});
return request
.then((res) => {
return res;
})
.catch((error) => {
console.log(error);
return error;
})
}
The problem is when backend is returning error code 422, the error object I am catching has no information about response body. Is there any way I can retrieve the error text?
I had this same issue and the answer (as per Axios >= 0.13) is to specifically check error.response.data:
axios({
...
}).then((response) => {
....
}).catch((error) => {
if( error.response ){
console.log(error.response.data); // => the response payload
}
});
See here for more details.
The "body" of an AXIOS error response depends from the type of response the request had.
If you would like full details about this issue you can see this blogpost: How to catch the body of an error in AXIOS.
In summary AXIOS will return 3 different body depending from the error:
Wrong request, we have actually done something wrong in our request (missing argument, bad format), that is has not actually been sent. When this happen, we can access the information using error.message.
axios.get('wrongSetup')
.then((response) => {})
.catch((error) => {
console.log(error.message);
})
Bad Network request: This happen when the server we are trying to reach does not respond at all. This can either be due to the server being down, or the URL being wrong.
In this case, we can access the information of the request using error.request.
axios.get('network error')
.then((response) => {})
.catch((error) => {
console.log(error.request );
});
Error status: This is the most common of the request. This can happen with any request that returns with a status that is different than 200. It can be unauthorised, not found, internal error and more. When this error happen, we are able to grasp the information of the request by accessing the parameter specified in the snippets below. For the data (as asked above) we need to access the error.response.data.
axios.get('errorStatus')
.then((response) => {})
.catch((error) => {
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
})
For those using await/async and Typescript
try {
const response = await axios.post(url, body)
} catch (error) {
console.log(error.response.data);
}
For react native it just worked for me
api.METHOD('endPonit', body)
.then(response => {
//...
})
.catch (error => {
const errorMessage = JSON.parse(error.request.response)
console.log(errorMessage.message)
})
We can check error.response.data as #JoeTidee said. But in cases response payload is blob type? You can get error response body with the below code.
axios({
...
}).then((response) => {
....
}).catch(async (error) => {
const response = error.response
if(typeof response.data.text === function){
console.log(await response.data.text()); // => the response payload
} else {
console.log(response.data)
}
});
I am returning a string from backend but expecting a json as response type. So I need to return an object instead of string for axios to process it properly.
In my case I wanted to retrieve a response 404 error message (body).
I got body with error.response.data but I couldn't display it because the type was ArrayBuffer.
Solution:
axios.get(url, { responseType: 'arraybuffer' }).then(
response => {...},
error => {
const decoder = new TextDecoder()
console.log(decoder.decode(error.response.data))
}
)
Related posts:
Converting between strings and ArrayBuffers

Categories