Best practice for handling Laravel response errors with Axios - javascript

I have been stuck deciding which method is the best practice for error handling. This involves sending requests from Axios client side, to Laravel server side. I've got two different methods for how I could potentially handle errors. Which would be best practice and why?
For this example we will use fetching profile information from the server in JSON format.
First method for error handling:
Server side would look like this, it returns two different JSON responses dependent on whether it's a success or error. Firstly a successful response, and then an error response.
//success response
return response()->json([
'success' => true,
'data' => []
], 200);
//error response
return response()->json([
'success' => false,
'error' => []
], 400);
Client side would look like this, this sends a request through Axios to fetch profile information and handles it accordingly to the HTTP response.
axios.get('/json/profile')
.then((response) => {
// handle success response (HTTP CODE 200)
})
.catch((error) => {
// handle error response (HTTP CODE 400)
});
The issue with this method is that in the console you'll get messages like this:
The second method for error handling:
This method avoids having messages like that in the console by always returning a HTTP response code of 200, with an error message if it's a failure.
The server side would look like this, the only thing that changes is the "success" value in the JSON server response.
//success response
return response()->json([
'success' => true,
'data' => []
], 200);
//error response
return response()->json([
'success' => false,
'error' => []
], 200);
The client side would look like this, it'll check the success part of the response whether it's true or false to determine whether it's an error or not.
axios.get('/json/profile')
.then((response) => {
if (response.success) {
//handle success
} else {
//handle error
}
});
Using the second method avoids any error information been passed into the users console.

Related

Why do I get "Fetch failed loading" when it actually worked?

I use the following code to POST the users position to my own backend-service via the Fetch API:
window.onload = () => {
let getPosition = (options) => {
return new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(resolve, reject, options);
});
};
getPosition().then(pos => {
const data = new FormData();
data.append('latitude', String(pos.coords.latitude));
data.append('longitude', String(pos.coords.longitude));
fetch('/', { // <--- error is thrown in this line
method: 'POST',
body: data
}).then(response => {
if (!response.ok) {
throw Error('Data sent - Network response NOT OK');
} else {
console.log('Data sent - Network response OK')
}
});
});
};
This works flawlessly and the backend-service sends back a positive empty response. Data sent - Network response OK is logged in the browser's console and everything is fine except that immediately after, this is logged:
How come I get a Fetch failed loading: POST even though the POST succeeded, the response is OK and the status code in the Chrome network tab is 200 OK? Is there something wrong with my code?
I had the same behaviour that you were seeing.
My server responded to the POST request with a 204 (Empty Response) and an empty (Content-Length=0) response.
I tried changing that to just respond with a "ok" message instead (edit: actually just returning the created object now) and the fetch error log disappeared.
It seems to me that fetch (at least in Chrome) erroneously logs "Fetch Failed" when the response body is empty, even if the request was successful.
This happens because you don't read the empty response body. I noticed this, because Django triggers a broken pipe error on the server side. To fix this, just read the empty body out.
async function fetchSomething() {
const response = await fetch(url, {
method: 'POST'
})
await response.text()
return response.ok
}

How can I get my fetch error to show an http status code?

I have a react component and I'm making a network call to set the state. Eventually I want to pass this down to other child components, but just getting the plumbing to work at the moment.
I'm trying to catch errors correctly when calling out to my backend (an express server in the app). I attempted to force an error by fetching data from an endpoint that doesn't exist. This should throw a 404 since it doesn't exist, right? How can I get that error surfaced in the catch statement? Right now my error is SyntaxError: Unexpected token < in JSON at position 0 at eval (app.js:61)
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
data: null
};
}
componentDidMount() {
fetch('/api/wrong_endpoint').then((data) => {
return data.json();
}).then((body) => {
this.setState({data: body})
}).catch(err => console.log(err));
}
render() {
console.log('logging the states');
console.log(this.state.data);
return (
<div>
<ContactList />
<ContactDetail />
<AddContactModal />
</div>
);
}
}
I'll try to go step by step
fetch method doesn't throw an error even if you get the 4xx or 5xx response codes. Please read about the Fetch API carefully, I believe you can find a lot of interesting you don't know about it.
You can easily check the response status as follows (please read about the Response object and its methods/properties):
fetch('/api/wrong_endpoint').then((response) => {
console.log('status code', response.status)
})
It's hard to say if your server really returns 404 code because I don't know your express setup. If you set some fallback handler like app.get('*', ...) then it might as well return 200 success code. You can check the response status and its body in devTools of the browser. But I believe it's better if you configure at least your /api router to return 404 error if the requested /api/... route isn't found.
What I'm really sure of is that your server returns some HTML layout in the response. And you try to parse it as JSON string via data.json() and of course you get the syntax error since it's not JSON (html layout starts with < symbol hence the error: SyntaxError: Unexpected token <)
Generally, if you are using the fetch API, errors 40x and 50x will not go into the subsequent blocks, as the promise from fetch only rejects network errors (not HTTP errors or anything else). Therefore, requesting for data from an 'incorrect' endpoint will be handled within the first then block.
I would recommend you to use check your http response body based on the Response.Ok property. Successful responses will be handled within that condition, whereas any other responses (ok: false) will be handled on the other statement.
fetch('/api/wrong_endpoint')
.then(response => {
console.log(response) // full response body
console.log(response.status); // get only the response.status
if (!response.ok) {
// http errors 40x and 50x will go into this statement
// do something to handle it
} else if (response.ok) {
// handles status code 200
}
})
.then(
// ...

Error: SyntaxError: Unexpected end of JSON input at fetch.then.response

I get this error every time I try to use the POST method in my API.
SyntaxError: Unexpected end of JSON input at fetch.then.response
When I use the GET method I get the data normally.
This is the code:
const teste = () => {
fetch("myURL/test", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
id: 1,
name: "Teste",
amount: 1,
value: 3
})
})
.then(response => response.json()) //Here is the error
.then(data => {
console.log(data);
})
.catch((err)=>console.log(err))}
Can someone help me? Thank you.
EDIT:
I just add this line to see the log:
.then(response => console.log(response))
Here is what I got:
Response {
type: "cors",
url: "myURL/test",
redirected: false,
status: 201,
ok: true,
…}
body: (...)
bodyUsed: false
headers: Headers {}
ok: true
redirected: false
status: 201
statusText: ""
type: "cors"
: "myURL/test"
__proto__: Response
It means that the page fetched at myURL/test does not responds with JSON content, or with malformed JSON content. This is not an error in your script, which is fine, this is an error with your server, that is not serving JSON content at myURL/test.
Also, note that servers might not respond similarly to GET requests and POST request for the same URL! If you fetch valid JSON from a GET request but, as you described, failed to fetch valid JSON from a POST request, your server might be serving different content depending on the request type. Investigate that.
Debug tips
Replace then(resp => resp.json()) by then(resp => resp.text()).then(console.log) to see what the served content looks like
Use Postman to simulate API calls, and see what the served content looks like, with more details
Examine the response with the developer tools:
In Chrome
Open the console (F12)
Go to the network tab
Click on the file server by myURL/test
Click on response: that will be the text content. It shoud be properly formatted JSON.
Basically GET method does not send your body object to the server in which you get the response. Only the POST action will send your body object to the server.
I assume that the object you wish to send is the problem. Either the server which you are trying to connect does not expects the body object as string or you should make sure you have parsed the JSON properly before processing.
Looks like the API you're calling, doesn't have a response body on this particular POST. Then when you call response.json() (converting response.body to json) it runs into error.
Or maybe the response is body is not a valid json body.
If you wanna handle this error more fashion way you could go like this:
tryGetJson = async (resp) => {
return new Promise((resolve) => {
if (resp) {
resp.json().then(json => resolve(json)).catch(() => resolve(null))
} else {
resolve(null)
}
})
}
https://github.com/github/fetch/issues/268#issuecomment-399497478
(for people coming later but dealing with this problem)
The problem is most probably server error or invalid URL but you can't see it because all examples on internet how to work with fetch are missing one important part - the server or network failure.
I think the correct way how to deal with fetch is test response if contains errors before conversion to json.
Check the part of the first then in example where it.ok is tested:
async function fetchData() {
return await fetch('https://your-server.com/some-NOt-existing-url/')
.then(it => {
if (!it.ok) {
throw `Server error: [${it.status}] [${it.statusText}] [${it.url}]`;
}
return it.json();
})
.then(receivedJson => {
// your code with json here...
})
.catch(err => {
console.debug("Error in fetch", err);
setErrors(err)
});
}
(note: it is just name convention borrowed from Kotlin, it makes JavaScript code shorter. It is alias for anything on left side of expression so in this case for response)

React/Redux - Handling errors and error codes

I am wondering what is the best way for me to be handling errors, and when I should be using error codes in response from my API in conjunction with an errors object.
I have just started building a multi-part form in react, every time a user moves onto the next step in the form I am sending a request to the API to validate the data before moving onto the next step, and sending back a response of true/false with an errors object if they exist.
My question is, when I am returning generic errors, e.g:
if( hasErr == true ) {
res.status(200).json({
success: false,
errors: errorsObject
});
}
Should I be sending a status of 200 back, or should I be sending back a response with an error code somewhere in the 400 range?
Currently on the client side when I am recieving a response, my basic redux action looks like this:
this.props.createlistingAction(this.state).then(
(res) => {
if( res.payload.success == true ) {
console.log('Success!');
} else {
console.log('We have errors!');
this.setState({ errors: res.payload.errors });
}
console.log(res);
//this.props.history.push('/'),
},
(err) => { // error
console.log(err.response);
this.setState({errors: err.response.data.errors, isLoading: false});
}
);
Is it ok to be handling errors like this? Or if you do suggest I send back a 400 error response, how should I be getting these errors when the payload only contains a generic error "Error: Request failed with status code 400 at createError"?
Thanks
You can also return a content with a status code different by 200.
So you should easily return the HTTP Status Code related to the error and the error details in the body of your response.

Error bubbles to console even though it is handled

I am using isomorphic-fetch to perform AJAX requests from my react-redux application. In my api middleware I have the following function which calls the external resource:
import fetch from 'isomorphic-fetch';
function callApi({ endpoint, method, body, params = {} }) {
let route = generateRoute(endpoint, params);
return fetch(route, generateFetchOptions(method, body))
.then(response => {
if (!response.ok) {
return Promise.reject(response);
}
return response.json();
});
}
The above function is called by the following piece of code:
return callApi(callAPI).then(
response => next(actionWith({
response,
type: successType,
statusCode: 200
})),
error => error.json().then(errorObject => {
return next(actionWith({
type: failureType,
statusCode: errorObject.statusCode,
error: errorObject.message || 'Something bad happened'
}));
})
);
If I reject with Promise.reject(response) the error is being handled by the error handler, but for some reason the error also bubbles to the browser console (in my case Chrome).
Here is a screenshot from the console which shows what is happening (api.js:34 is the second line of the callApi method):
This is the usual behavior (in probably every browser?) when hitting an error during an HTTP request (no matter whether a linked image cannot be found, or an XHR fails). No matter if and how you handle those errors, they will always be logged to the console. There is no way to suppress this behavior.
References:
Provide a way not to display 404 XHR errors in console
How can I stop jQuery.ajax() from logging failures to the console?

Categories