How to handle null response in fetch api - javascript

I building a react-native app and using fetch api for handling server request, it is working fine if json returned from the server is not null, but if the response from the server is null it will give me an error-"Json Parse error:Unexpected EOF", below is the code used by me for fetch, I tried to set break-point while debugging to see what is coming in response when null is returned from the server, I am unable to find something on which I can put some check and see if response is null before parsing it, so need help
return fetch(url, //service url{
method: type, // get or post
headers: {
'Accept': 'application/json',
'Content-Type': contentType,
},
body: data //some input parameters
}).then((response) => {
return response.json();
})
.then((responseJson) => {
request.onSuccess(responseJson); // success callback
})
.catch((error) => {
request.onError(error); // error callback
console.error(error);
});

There is a good answer here, but in my case I needed access to the response object after response.text() returns:
function buildResult(response) {
// response.json() crashes on null response bodies
// return {
// data: response.json(),
// identityToken: response.identityToken // sliding expiration...
// };
return new Promise((resolve, reject) => {
response.text().then(body => {
resolve({
data: body.length ? JSON.parse(body) : null,
identityToken: response.identityToken // sliding expiration...
});
}).catch(err => {
reject(err);
});
});
}
//
// the api fetch function
//
function apiFetch(url) {
return fetch(url)
.then(checkStatus)
.then(parseIdentityToken)
.then(buildResult);
}

If the json response null Instead of using response.json() use response.text()
fetch(path)
.then(function (response) {
return response.text()
}).then(function (data) {
resolve(data.length == 0 ? null : JSON.parse(data))
}).catch(err => {
reject(err);
})

If you want to сheck the response request for emptiness:
const response = await fetch(url, options); // your url and options
if (response.ok) {
const contentType = response.headers.get('content-type');
if (contentType && contentType.indexOf('application/json') !== -1) {
const json = await response.json();
successCb(json); // Write your script.
} else {
successCb(); // if the request is successful but the response is empty. Write your script.
}
}

Related

Fetch request not returning any data in console, or on front end

When I submit my form, I am not returning any data, not even in my console. I am trying to return details from WHOIS regarding the URL that is searched, and am getting nothing back.
Can anyone provide any advice as to why this might be the case?
Here is my front end script tag, after my form:
document.getElementById('search').addEventListener('submit', function(e) { e.preventDefault(); getDetails(); })
async function getDetails(url = `http://localhost:3000/lookup/${url}`, data = {}) {
return new Promise(function (resolve, reject) {
fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'text/html'
},
body: JSON.stringify(data)
}).then(async response => {
if (response.ok) {
response.json().then(json => resolve(json))
console.log(data);
} else {
response.json().then(json => reject(json))
}
}).catch(async error => {
reject(error)
})
})
}
On my express backend I am using req.params.url if that helps provide any context at all...
My Status Code is 200, and all appears to be normal in the Headers tab...
You have a mix of promise and async syntax, which is confusing, let's translate it first by unpicking the promise and then into await (if you can use async then do, it's easer than Promise/then):
document.getElementById('search').addEventListener(
'submit',
function(e) {
e.preventDefault();
getDetails();
});
async function getDetails(url = `http://localhost:3000/lookup/${url}`, data = {})
{
// Fetch will throw an exception if it can't connect to the service
const response = await fetch(url, {
method: 'GET',
headers: { 'Content-Type': 'text/html' },
body: JSON.stringify(data)
});
if (response.ok)
return await response.json();
// We could connect but got an error back from the service
// There may not be a response body, so response.json() or .body() might crash
throw new Error(`Error from server ${response.status}: ${response.statusText}`;
// We don't need catch, as any exception in an await will cascade up anyway
}
This makes it much more readable, and it's apparent that getDetails doesn't make any changes itself, it just returns the JSON from the service. The fix needs to be in the event listener - it needs to do something with that result:
document.getElementById('search').addEventListener(
'submit',
async e => {
e.preventDefault();
const searchResult = await getDetails();
// Do something to show the results, populate #results
const resultsElement = document.getElementById('results');
resultsElement.innerText = JSON.stringify(searchResult);
});
You are mis-using async/await. Try this:
async function getDetails(url = `http://localhost:3000/lookup/${url}`, data = {}) {
const response = await fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'text/html'
},
body: JSON.stringify(data)
});
const json = await response.json();
if (response.ok) {
console.log(json);
return json;
} else {
throw new Error(json);
}
}
In essence await is a replacement for then (but you can only use it in functions marked with async).

axios transformResponse returning undefined

With the following I get the expected response.data value:
axios({
method,
url
}).then(response => { console.log(response) })
However, when I add the transformResponse property as follows I get a response.data value of undefined:
axios({
method,
url,
transformResponse: [(data) => {
return data
}]
}).then(response => { console.log(response) })
Can someone please tell me what I'm missing here! Thanks
I suggest you to use interceptors since they are more clean
Interceptors work as a middlware on your requests
remove transformResponse and add this
axios.interceptors.response.use(function (response) {
return response.data;
});
axios({
method,
url,
}).then(response => { console.log(response) })
You can check the below sample to safe parse. transformResponse get data as raw staring so u can parse it.
const instance = axios.create({
baseURL: baseURL,
transformResponse: [
(data) => {
let resp;
try {
resp = JSON.parse(data);
} catch (error) {
throw Error(
`[requestClient] Error parsingJSON data - ${JSON.stringify(
error
)}`
);
}
if (resp.status === "success") {
return resp.data;
} else {
throw Error(`Request failed with reason - ${data}`);
}
},
],
});
Else you can use an interceptor to simplify it.
//const axios = require("axios");
const jsonInterceptor = [
(response) => response.data,
(error) => Promise.reject(error),
];
function jsonClient() {
const client = axios.create();
client.interceptors.response.use(...jsonInterceptor);
return client;
}
// Page 1
const jhttp = jsonClient();
jhttp
.get("https://jsonplaceholder.typicode.com/todos/1")
.then((data) => console.log(data));
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.2/axios.min.js"></script>
For anyone wondering here is the response:
axios.get(url, {
headers: {
'Content-Type': 'application/json'
},
transformResponse: axios.defaults.transformResponse.concat((data) => {
console.log(data) // this should now be JSON
})
})
from https://github.com/axios/axios/issues/430#issuecomment-243481806

Null object returned when fetching from Shippo API

I'm trying to get a list of all my shipments from the Shippo API. I'm using the fetch() API to get the data.
This is my code:
fetch("https://api.goshippo.com/shipments/", {
method: "GET",
withCredentials: true,
headers: {
"Content-Type": "application/json",
Authorization: `ShippoToken ${shippoToken}`
}
})
.then(response => {
console.log(response);
return response.json();
})
.then(data => {
console.log(data);
return data;
})
.catch(() => {
console.log("Can't access url");
});
}
Here is a picture of what's happening in the console :
I'm getting a valid response but the object returned from the response's JSON has null next, null previous, and an empty array of results even though I have shipments in my account. I'm currently using a shippo_test token as my shippoToken.

Trying to get data an 'status' from fetch Promise

I am trying to make a generic Fetch method for my React project. The plan is to pass my fetch method a url, and some config data (method, header etc). And then return less technical data to my calling methods. I'd like to return the data from the api call, and the payload, which is json data.
return fetch(URL, config)
.then((response) => {
console.log('Fetch - Got response: ', response);
return response;
})
.then((response) => {
console.log('Json: ', response.status);
const result = { data: response.json(), status: response.status };
return result;
})
.catch((e) => {
console.log(`An error has occured while calling the API. ${e}`);
reject(e);
});
This is my initial attempt, but I'm not quite sure what I'm doing.
my console log that logs 'response', contains the response from the API call:
body: (...)
bodyUsed: true
headers: Headers {}
ok: true
redirected: false
status: 200
statusText: "OK"
type: "cors"
url: "http://localhost:49487//api/xxx/yyy"
So the API call completes, and I get a 200 status.
The line:
console.log('Json: ', response.status);
return 200 as expected.
What I have been doing before is
return response.json()
And then my calling class gets the paylad, but no status. What I am trying to do is return my payload, AND the status.
So I attempted to change it to this:
const result = { data: response.json(), status: response.status };
return result;
But my calling app now sees:
data: Promise {<resolved>: Array(9)}
status: 200
I was expecting to get data: MyPayloadArray, status: 200
I think I'm misunderstanding promises here. (I'm quite green with them).
My data accessor that uses my Fetch method:
static GetAll() {
return new Promise((resolve, reject) => {
const request = {
method: 'GET',
URL: `${Server.ApiURL}/api/admin/clients`,
};
fetchData(request)
.then((result) => {
console.log('GetAll sees response as ', result);
resolve(result);
})
.catch((error) => {
reject(new Error(error));
});
});
}
I'm trying to call my data accessor class, like this:
componentDidMount() {
ClientDataAccessor.GetAll()
.then((response) => {
console.log('Got list!', response);
this.setState({ clients: response, isLoading: false });
})
.catch((error) => {
console.log('Got error on list screen', error);
});
}
How can I get just the status, and the payload back to my DataAccesor class? I think I'm just screwing up the Promises... But not sure of the best pattern here.
I'm going UI class, onComponentDidMount -> DataAccessor.GetAll -> FetchData. I think I'm abusing the Promise somewhere.
The issue here is that response.json() returns another promise. You would have to resolve that promise object yourself and return the object you are looking for.
return fetch(URL, config)
.then((response) => {
console.log('Fetch - Got response: ', response);
return response;
})
.then((response) =>
response.json()
.then( (data) => { data, status: response.status } )
)
.catch((e) => {
console.log(`An error has occured while calling the API. ${e}`);
reject(e);
});
Very ugly...
Why not move it to async/await funtion? All browsers support it at this stage...
async myFetch(URL, config) {
try {
const response = await fetch(URL, config);
console.log('Fetch - Got response:', response);
const data = await response.json();
console.log('Fetch - Got data:', data);
return { data, status: response.status }
}
catch (e) {
console.error(`An error has occured while calling the API. ${e}`);
throw e;
}
}
Just be aware, in both cases, that your function returns another promise.
I think you can solve your issue using Promise.resolve, chaining promise to obtain your result object:
...
.then(response => {
var status = response.status;
return Promise.resolve(response.json())
.then(data => ({ data, status }))
})
The Promise.resolve can take a Promise as parameter and return a promise that will flatten the chain, so you can get the value of the json parse and work with it.
You need to further resolve res.json() to get the info. You can do something like this:
return fetch(URL, config)
.then((response) => {
console.log('Fetch - Got response: ', response);
return response;
})
.then(response =>
response.json().then(json => ({
status: response.status,
json
})
))
.then(({ status, json }) => {
console.log({ status, json });
return { data: json, status: status };
})
.catch((e) => {
console.log(`An error has occured while calling the API. ${e}`);
reject(e);
});
The neat version of above could be:
return fetch(URL, config)
.then(response => response.json()
.then(json => ({ status: response.status, data: json }) )
)
Note: Removed the reject(e) from inside of catch, because it is redundant.

Handling failed API response in fetch

In my application, I have a simple fetch for retrieving a list of users which sends over an authentication token to an API
fetch("/users", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({
token: this.props.getUser().token
})
})
.then(res => res.json())
.then(users => this.setState({ users }))
.catch(err => {
console.error(err);
});
However, the API may return a 401 error if the token is expired.
How do I handle it properly in the fetch so that the state is only set when the response is succesful?
A cleaner way to handle success/error of a fetch response would be to use the Response#ok readonly property
https://developer.mozilla.org/en-US/docs/Web/API/Response/ok
fetch('/users').then((response) => {
if (response.ok) {
return response.json();
}
throw response;
}).then((users) => {
this.setState({
users
});
}).catch((error) => {
// whatever
})
res inside callback function in your first .then function contains a key called status which holds the request status code.
const url = 'https://api.myjson.com/bins/s41un';
fetch(url).then((res) => {
console.log('status code:', res.status); // heres the response status code
if (res.status === 200) {
return res.json(); // request successful (status code 200)
}
return Promise.reject(new Error('token expired!')); // status code different than 200
}).then((response) => console.log(response));

Categories

Resources