I have a function in my front end app which calls my node.js backend server:
Client function:
this.geocode = (placeName) => {
const url = '/api/twitter/geocode?' + 'query=' + encodeURIComponent(placeName);
return fetch(url)
.then(processResponse)
.catch(handleError)
}
// API Helper methods
const processResponse = function (response) {
if (response.ok) {
console.log(response);
return response.json()
}
throw response;
}
const handleError = function (error) {
if (error.json) {
error.json().then(error => {
console.error('API Error:', error.message || error)
})
} else {
console.error('API Error:', error.message || error)
}
}
Server route:
app.get('/api/twitter/geocode', (req, res) => {
var parameters = {
query: req.query.query
}
Twitter.get('geo/search', parameters)
.then(response => {
console.log("RESPONSE:")
console.log(response);
// check if there is a place for the given query
if(response.date.places.length > 0){
res.send(response.data.result.places[0].bounding_box.coordinates[0][0]);
}
res.send()
})
.catch(e => res.status(500).send('Something broke!')
)
});
There is a problem when placeName is the name of a place that doesn't exist (or at least that Twitter doesn't know about). The console.log(response) in the backend shows me that such a request to the Twitter api leads to a return message without place data:
{ data:
{ result: { places: [] },
query:
{ url: 'https://api.twitter.com/1.1/geo/search.json?query=FOOBARTOWN',
type: 'search',
params: [Object] } },
resp:
/*etc...*/
As you can see, places is an empty list. This response causes a crash in my frontend. I would like to know why. Look at the error message:
const handleError = function (error) {
if (error.json) {
> error.json().then(error => {
console.error('API Error:', error.message || error)
})
} else {
And some other console outputs in the browser:
GET http://localhost:3000/api/twitter/geocode?query=FOOBARTOWN 500 (Internal Server Error) (model.js:197)
Uncaught (in promise) SyntaxError: Unexpected token S in JSON at position 0
at handleError (model.js:214)
It seems like we are getting error 500. But why? My server hasn't failed. There's no error message in the node.js console.
Also, it seems like the program ends up in handleError, and then fails to convert the error to json.
Why does it go to handleError? What's the error?
Why does it say my server failed (error 500)?
How can I make sure that if this.geocode gets an empty message, I don't crash, and return that empty message (so I can check for an empty message and act appropriately in my React.js component)?
Why does it go to handleError? What's the error?
Your server is sending a 500 status code, with Something broke! as response body.
An when you try to use res.json() on a non JSON string you get:
Uncaught SyntaxError: Unexpected token S in JSON at position 0
The line if(error.json) is not doing what you think it does. handleError is being called when response.ok is false, since you're throwing the fetch response object otherwise, in that case error argument will be a fetch response that implements Body, which has a json method, even if the body isn't a JSON, which is your case.
Your handleError can be written like this, where you will handle fetch errors and non 2xx responses.
const handleError = async function(error) {
if(error instanceof Response) {
if(error.headers.get('content-type').includes('application/json'))
error = await error.json();
else error = await error.text();
}
console.error('API Error:', error.message || error)
}
Why does it say my server failed (error 500)?
Place a console.log(e) on Twitter.get().catch and you'll find out.
Your Twitter.get().then is also wrong, since you're sending the response twice.
if(response.date.places.length > 0){
res.send(response.data.result.places[0].bounding_box.coordinates[0][0]);
}
res.send()
Should be:
if(response.date.places.length > 0)
return res.send(response/*...*/);
res.send()
Related
I am using an API that if an internal server error occurs the details of the error are returned in JSON format.
Currently, I have the following code that can handle a successful response but in the case of an HTTP 500 response, the status text is logged in the console:
function checkStatus(response) {
if (!response.ok) {
throw new Error(response.statusText);
}
return response;
}
fetch("/api/url")
.then(checkStatus)
.then(response => response.json())
.then(data => {
// process success JSON here
})
.catch(err => {
console.log(err);
});
How would I handle the JSON that is returned in the HTTP 500 response, so the properties could be used in document.getElementById(id).innerHTML assignments?
The json contained in the reponse is always in response.json() even in case of an error 500.
In your example you are throwing an error containing only statusText.
You could instead do something like that:
function checkStatus(response) {
if (!response.ok) {
response.json().then((jsonError: any) => {
// your code to handle the json, for example:
if (error.status === 500) {
throw new HttpError('Something went wrong', jsonError);
}
}
}
return response;
}
You could for example create a new class of error that takes in a property corresponding to your jsonError:
export class HttpError extends Error {
public jsonError: any;
constructor(error: Error, jsonError: any) {
super(error.message);
this.jsonError = jsonError;
}
}
Then you could use the jsonError property in your catch() method.
I am creating a mern authentication project and am stuck on a problem. When the information is sent in my register page an add user function is called on the front end
async function addUser(user) {
const config = {
headers: {
"Content-Type": "application/json",
},
};
try {
await axios.post("/users/register", user, config);
} catch (err) {}
}
Which calls this function in my back end
exports.addUser = async (req, res, next) => {
try {
const { name, email, password } = req.body;
let errors = [];
// Check required fields
if (!name || !email || !password) {
errors.push("All Fields Need To Be Filled");
}
//Check Password Length
if (password.length < 6) {
errors.push("Password Needs To Be At Least 6 Characters Long");
}
if (errors.length > 0) {
return res.status(201).json({
success: false,
errors,
});
} else {
return res.status(201).json({
success: true,
data: req.body,
});
}
} catch (error) {
return res.status(500).json({
success: false,
error,
});
}
};
And my route
router.route("/register").post(addUser)
My question is how to get the json from the node.js function in react.js. For example if there is an error how do I get this
return res.status(201).json({
success: false,
errors,
in my front end. Since this is not a get request I can't access the errors with
const res = await axios.get("/");
If anybody knows what to do it would mean a lot if you helped
First off, I would recommend changing the code as a response of 201 is considered a successful "Created" response (mozilla docs).
Then, if you are using axios, you need to create an interceptor in your frontend side that will handle the response errors. Check the documentation here.
Basically you can leave the response part empty, and then throw an exception in the error function so you can handle it from the place you are making the call.
axios.interceptors.response.use(
(response) => response,
(error) => {
// you can handle the error here or throw an error
throw error;
}
})
I recommend that you leave the interceptor and the base axios call in a separate file so it's easier for you to handle the calls and the interceptor.
I'm trying to implement validation on my Node.js back-end so whenever the data doesn't pass the validation, I'm sending this to the front-end:
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
so that I could render the errors on the front-end. Sadly, when I open the console, I only see:
POST http://localhost:3000/login 400 (Bad Request)
as opposed to an object that would contain config, data, headers, request and status. So I am wondering how the hell am I supposed to access the errors object I'm returning to the front-end. I'm following express-validator's docs and this is how they do it as well - https://express-validator.github.io/docs/index.html
You just need to retrieve and parse the response body, even for non-successful requests.
Here's an example using fetch but the approach would be similar using other libs
const doFetch = async (url) => {
const res = await fetch(url, {
// method, headers, body, etc
})
if (!res.ok) {
if (res.status === 400) {
throw await res.json() // this will parse the JSON response body
}
// handle other errors
throw { errors: [ res.statusText ] } // conform to a standard format
}
// handle success
}
doFetch('http://example.com').catch(({ errors }) => {
console.error(errors)
})
Using axios, I'm making GET requests to multiple links to test to see if they are broken links. If the GET requests returns an error, I want to log this error to the console and send it to the client side with socket. However, on some links (not all), I get an UnhandledPromiseRejectionWarning.
// check if the link is dead
axios.get(linkHref, {
auth: {
username: USERNAME,
password: PASSWORD
}
})
.then( (response) => {
if (response.status != 200) {
resultData = {
text: linkText,
url: linkHref,
statusCode: String(response.status)
}
console.log(resultData);
io.sockets.emit("result", resultData); // send link to client-side
}
})
.catch( (error) => {
if (error) {
resultData = {
text: linkText,
url: linkHref,
statusCode: String(error.response.status) // this is where the error is
}
console.log(resultData);
io.sockets.emit("result", resultData); // send link to client-side
}
});
I expect it work correctly and return the status code of the error but sometimes I get a UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'status' of undefined error. But with my if statement in the .catch, I am checking to make sure that there is an error and that it is not undefined so I don't understand why I'm getting this error.
EDIT: Also, this works (does not show me that error) when I run it on my Mac but not in Windows ... why?
TypeError: Cannot read property 'status' of undefined The response property on error.response is not defined eg error = { response: undefined }. Therefore, referencing any properties on it throws an error. error.response[property]
Also on failed/hanging http requests the error argument is different. Checkout this gist axios-catch-error. The error argument will have different properties for this case.
hey can you check by validating whether you are getting the response property on the error object in the if else condition
.catch( (error) => {
if (error.response) {
//rest of the code...
}
});
I'm calling an API that defines the statusCode from data instead of the response code:
{
data: {
statusCode: 422,
message: "User's not found"
},
status: 200
}
In my axios get request it's getting the status code from the status instead in data.
return axios.get(`${process.env.BASE_URL}/users`)
.then(response => {
console.log(response);
}).catch(err => {
console.log(err.message);
});
I'm getting the response but it should go to catch since it's 422.
How can I refer to the statusCode of the data response so that if it's not 200 it should go to catch statement
You can intercept the response, inspect the data and throw a custom error in this case:
// Add a response interceptor
axios.interceptors.response.use(function(response) {
if (response.data && response.data.statusCode && !(response.data.statusCode >= 200 && response.data.statusCode < 300)) throw new Error()
return response;
}, function(error) {
return Promise.reject(error);
});
// Make a GET request
axios.get(url)
.then((data) => {
console.log('data', data)
})
.catch((e) => {
console.log('error', e)
})
This way you configure your axios instance so you dont have to repeat yourself for every single request in your app
Also, you can override the status using following code. But since status validation has already executed, it will not throw errors on bad status codes
// Add a response interceptor
axios.interceptors.response.use(function(response) {
if (response.data && response.data.statusCode) response.status = response.data.statusCode
return response;
}, function(error) {
return Promise.reject(error);
});
You can handle with standard if statement inside the .then()
return axios.get(`${process.env.BASE_URL}/users`)
.then(response => {
if(response.data.statusCode===442){
...//custom error handling goes here
}else{
...//if statusCode is a success one
}
}).catch(err => {
console.log(err.message);
});
Check the response.data.statusCode value, if it is 442 then you should ideally throw an Error and let it be handled in the .catch callback.
return axios.get(`${process.env.BASE_URL}/users`)
.then(response => {
if(response.data.statusCode===442){
throw new Error(response.data.message); //using throw instead of Promise.reject() to break the control flow.
}else{
//return the data wrapped in promise
}
})
.catch((err) => {
console.log(err.message);
return Promise.reject(err.message);
});