Fetch Api javascript receive JSON when error - javascript

I post data to server using fetch:
fetch(url, {
method: 'post'
})
.then(status)
.then((data) => {
console.log(data);
}).catch((error) => {
console.log('error: ' + error);
});
});
This is status method
const status = (res) => {
console.log(res);
if(res.status >= 200 && res.status < 300) {
return Promise.resolve(res.json());
} else {
return Promise.reject(res.json());
}
}
If code is 200 then it works fine (I receive my JSON). But when it is not, I catch the error and log it. It shows Unauthorized but I expect that I receive my error JSON. Something like this
{
"status": 400,
"error": "pass is not corret"
}
How can I catch my Json error?

At this point I'd think that you'd just need to return the JSON from the response. Judging by your conditional though you're returning the JSON regardless?:
const status = (res) => {
console.log(res);
if (res.status >= 200 && res.status < 300) {
return res.json();
}
}

res.json() returns a Promise. When you call Promise.reject you can only pass it a reason for the rejection. Like a string, object or Error describing why you're rejecting.
I would change the code a bit to
const status = (res) => {
return new Promise((resolve, reject) => {
res.json().then((json) => {
if(res.status >= 200 && res.status < 300) {
resolve(json);
}
else {
reject(json)
}
});
});
}
Source:
MDN - Promise.resolve
MDN - Promise.reject

Related

Problem in implementing Axios.get in Reactjs

I am new to react and I want to make an Axios.get() request based on a function parameter. This is what I tried.
const mentorName = (value) => {
try {
Axios.get(
`${BASE_URL}/api/v1/consultations/${value}`
).then(res => {
if (res.status !== 200 || res.data.status !== "success") {
console.log(res)
return
}
})
} catch (error) {
console.log(error)
}
}
But It didn't work as res was not printed in console. What is wrong in this?
The code that worked fine is:
const mentorName = (value) => {
try {
const res = Axios.get(
`${BASE_URL}/api/v1/consultations/${value}`
)
if (res.status !== 200 || res.data.status !== "success") {
console.log(res)
return
}
} catch (error) {
console.log(error)
}
}
The below code worked fine but returns information wrapped in a promise. How to access it now because res.data is not a valid property.
Can you try this with async/await.
import axios from 'axios';
const mentorName = async value => {
try {
const res = await axios.get(`${BASE_URL}/api/v1/consultations/${value}`);
console.log(res);
} catch (error) {
console.log(error);
}
};
In the console.log inside try block you can check for the api response.
const mentorName = (value) => {
try {
Axios.get(
`${BASE_URL}/api/v1/consultations/${value}`
).then(res => {
if (res.status !== 200 || res.data.status !== "success") {
console.log(res)
return
}
})
} catch (error) {
console.log(error)
}
}
above code doesn't print because of the if condition, it is likely that the status is going to be 200 most of the time and anything apart from 200 would drill down to catch block
the reason it is printing promise in below code is because, it is a promise waiting to be fulfilled and the comparison / condition you have put up is very much fine because res is a promise and res.status is undefined
const mentorName = (value) => {
try {
const res = Axios.get(
`${BASE_URL}/api/v1/consultations/${value}`
)
if (res.status !== 200 || res.data.status !== "success") {
console.log(res)
return
}
} catch (error) {
console.log(error)
}
}
tweak the code to include an else block and you can always see something printed in console
const mentorName = (value) => {
try {
Axios.get(
`${BASE_URL}/api/v1/consultations/${value}`
).then(res => {
if (res.status !== 200 || res.data.status !== "success") {
console.log(res)
return
} else {
console.log(res);
}
})
} catch (error) {
console.log(error)
}
}
I do not recommend using async/await due to one single and pressing reason, that the UI thread is put on hold until the async call is resolved. just to make it look like a synchronous call. stick on to the promise way.

How to handle this fetch() TypeError?

I am trying to print to the console (in order to later handle those edge cases) the response status of my fetch query. However, the only console.log call that works is the one in the 'breaches' function. I get no errors when an account exists in the HIBP db, but I get a 'Request failed: TypeError: response.json is not a function at json' error when the account is not in the db. What am I doing wrong? I got the error handling code from the Google Web Dev articles.
function createNode(element) {
return document.createElement(element);
}
function append(parent, el) {
return parent.appendChild(el);
}
function status(response) {
if (response.status >= 200 && response.status < 300) {
return Promise.resolve(response)
console.log('all is good');
} else if (response.status == 404) {
return Promise.resolve(response.statusText)
console.log('no breaches');
} else if (response.status == 400) {
return Promise.resolve(response.statusText)
console.log('bad req');
} else {
return Promise.reject(new Error(response.statusText))
}
}
function json(response) {
return response.json()
}
var account = document.getElementById('account'),
results = document.getElementById('results');
account.addEventListener("keyup", keyupEvent);
function keyupEvent() {
event.preventDefault();
if (event.key === "Enter") {
fetch('https://haveibeenpwned.com/api/v2/breachedaccount/' + account.value, {
timeout: 1500,
userAgent: 'test'
})
.then(status)
.then(json)
.then(function(breaches) {
console.log('Status Code: ' + breaches.status);
let span = createNode('span');
return breaches.forEach(function(check) {
span.innerHTML = `${check.Name}<br/>`;
append(results, span)
})
}).catch(function(error) {
console.log('Request failed:', error);
});
}
}
Your status function returns (a promise of) the status text for responses that are 400s or 404s. Your promise chain consuming the fetch result doesn't handle that possibility; it assumes it gets the response object.
You probably want to reject on 400s or 404s rather than resolving, but if not, you'll need to branch in your then handler expecting to read JSON.
Your code consuming the breaches is also overwriting the same span and repeatedly appending it; it will end up appended only once, with the last breach's information. And the append function doesn't provide any useful abstraction over just calling appendChild.
If the API really returns 404 for "no breaches" (blech), then I'd get rid of createNode and append, change status to this:
function status(response) {
if (response.ok) {
return response.json();
} else if (response.status === 404) { // If the API *really* returns
return []; // a 404 for "no breaches"
} else {
throw new Error(response.statusText);
}
}
and then:
fetch('https://haveibeenpwned.com/api/v2/breachedaccount/' + account.value, {
timeout: 1500,
userAgent: 'test'
})
.then(status)
.then(breaches => {
// No `return` here, the chain isn't passed on and there
// aren't any further resolution handlers
breaches.forEach(check => { // Or a for-of loop
const span = document.createElement("span");
span.innerHTML = `${check.Name}<br/>`;
results.appendChild(span);
});
}).catch(error => {
console.log('Request failed:', error);
});
Separately: Your status function suggests that you don't realize that then (and catch) create new promises. There's no reason for your status function to create any promises if it's only going to be used as a then handler. It should just a return a value (the promise created by then will resolve with that value) or throw an error (the promise created by then will reject with that error):
// This is equivalent to your current `status` function (assuming it's always
// used as a `then` callback)
function status(response) {
if (response.ok) { // if (response.status >= 200 && response.status < 300) {
// all okay
return response;
} else if (response.status == 404) {
// no breaches
return response.statusText;
} else if (response.status == 400) {
// bad request
return response.statusText;
} else {
throw new Error(response.statusText);
}
}
(I removed the console.log lines that were after the return in each branch, as they were unreachable.)

How to properly check and log http status code using promises and node.js?

I am new to JavaScript and very new to node.js framework, just started using it a few days ago. My apologies if my code is nonsensical, the whole idea of promises and callbacks is still sinking in. That being said my question is the following I am trying to figure out if certain request to websites are successful or cause an error based on the range of their status code response. I am working with an array of websites and what I've done so far is below, I do however get a TypeError: Cannot read property 'then' of undefined on my local machine with node.js installed and can't figure out why.
const sample = [
'http://www.google.com/',
'http://www.spotify.com/us/',
'http://twitter.com/',
'http://google.com/nothing'
]
const http = require('http')
const getStatusCodeResult = (website) => {
http.get(website, (res) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
let statusCode = res.statusCode
error = statusCode >= 400 && statusCode <= 500 ? `error: ${website}`: null
if (error) {
reject(error)
} else if (statusCode >= 200 && statusCode <= 300) {
resolve(`Success: ${website}`)
}
}, 0)
})
})
}
// LOOP PROMISES
const getAllStatusCodeResult = (websites) => {
websites.forEach((website) => {
getStatusCodeResult(website)
.then((result) => {
console.log(result)
})
.catch(error => {
console.log('error', error)
})
})
}
getAllStatusCodeResult(sample)
Ideally I would want the result to be printed as the example below, but for now I am just using console.log to figure out if the code even works.
// Example Printout
{
success: ['https://www.google.com/', 'https://www.spotify.com/us/',
'https://twitter.com /' ],
error: [''http://google.com/nothing']
}
You mixed up the first two lines. The new Promise wrapper that gets you the value to return needs to be on the outside, and the http.get call should be inside its executor callback. Also you don't really need that timeout:
function getStatusCodeResult(website) {
return new Promise((resolve, reject) => {
http.get(website, (res) => {
let statusCode = res.statusCode,
error = statusCode >= 400 && statusCode <= 500 ? `error: ${website}`: null
if (error) {
reject(error)
} else if (statusCode >= 200 && statusCode <= 300) {
resolve(`Success: ${website}`)
}
})
})
}
Using util.promisify(), you can convert http.get() into a promise-based asynchronous method, but first there's some preparation to do since it does not follow the convention of callback(error, response) { ... }:
const http = require('http')
const { promisify } = require('util')
// define a custom promisified version of `http.get()`
http.get[promisify.custom] = (options) => new Promise(resolve => {
http.get(options, resolve)
});
// convert callback to promise
const httpGet = promisify(http.get)
async function getStatusCodeResult(website) {
const res = await httpGet(website)
const status = res.statusCode
const message = `${http.STATUS_CODES[status]}: ${website}`
if (status >= 400) {
throw message
} else {
return message
}
}
In addition, you can use http.STATUS_CODES to get the the appropriate message for each possible statusCode rather than returning a vague Error or Success.

Return promise from a http request

Making a precheck to see if images in an api actually exists. I'm using 'q' as my promise library and 'request' lib to make the http request. I'm trying to return an array of all the successfully requested images as so:
.then((data)=> {
return q.all(data.map((elem) => {
let deferred = q.defer()
request(`https://address/path/${elem}.jpg`, (error, response) => {
if (!error, response.statusCode === 200) {
deferred.resolve(elem)
}
})
return deferred.promise
})
)
})
.then((result)=> {
console.log('Array of existing images', result)//Result should contain all image id's which had status code 200
})
I would expect this to return all results which had a request status of 200. With q.all() I expect the function to return once all promises are fulfilled.
I think, you shuld add one chain, where you clean non 200 images
for exemple, lik that
.then((data)=> {
return q.all(data.map((elem) => {
let deferred = q.defer()
request(`https://address/path/${elem}.jpg`, (error, response) => {
if (!error) {
deferred.resolve({img:elem, status : response.statusCode})
}else{
deferred.reject({elem: 'error'})
}
});
return deferred.promise
})
)
})
.then((result)=> {
let onlyGoodImages = [];
result.map((elem) => {
if (elem.status == 200){
onlyGoodImages.push(elem.img);
}
});
return onlyGoodImages;
}
)
.then((result)=> {
console.log('Array of existing images', result)//Result should contain all image id's which had status code 200
})

HTTP Promise - Handling Errors

I am trying to find a nice way of handling http responses that I consider an error. I am using fetch in React Native. Here is my code.
loginRequest(url) {
return fetch(url, {
method: 'post',
headers: {
'Content-Type': 'application/x-www-form-urlencoded;'
},
....
})
.then(response => {
return this.processResponse(response);
});
}
Then...
processResponse(response) {
if (response.status === 200) {
return response.json();
} else {
let error = new Error(response.status);
error.response = response.json(); // This is the problem
error.status = response.status;
throw error;
}
},
And the above are called like this:
return ApiRequests.loginRequest(username, password)
.then(json => {
dispatch(Actions.loginSuccess(json, username, password));
})
.catch(error => {
dispatch(Actions.loginFailure(error));
});
};
The idea is that I can easily handle all the errors separately (we assume anything but 200 error), within the catch. The problem is that response.json() returns a promise, so assigning it to error.response is not working. I need to keep track of http status code and the response body.
How about this:
processResponse(response) {
if (response.status === 200) {
return response.json();
} else {
return response.json().then((data) => {
let error = new Error(response.status);
error.response = data;
error.status = response.status;
throw error;
});
}
}

Categories