I want to display error message from my API, problem is that I can't reach that error if I check for response.ok, it returns Fetch error, not the one from API..
If I don't use if(response.ok)... it returns the error from API but it dispatches the success action.
Here is the example, login action:
export const signIn = data => dispatch => {
dispatch({
type: SIGN_IN
})
fetch(API_URL+'/login', {
method: 'POST',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify(data),
})
.then( response => {
if (!response.ok) { throw response }
return response.json() //we only get here if there is no error
})
.then( json => {
dispatch({
type: SIGN_IN_SUCCESS, payload: json
}),
localStorage.setItem("token", 'Bearer '+json.token)
localStorage.setItem("user", JSON.stringify(json.user))
})
.catch( err => {
dispatch({
type: SIGN_IN_FAILED, payload: err
})
})
}
This is the code for action that dispatches the right message but as success action, not as failed one..
export const signIn = data => dispatch => {
dispatch({
type: SIGN_IN
})
fetch(API_URL+'/login', {
method: 'POST',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify(data),
})
.then( response => response.json())
.then( json => {
dispatch({
type: SIGN_IN_SUCCESS, payload: json
}),
localStorage.setItem("token", 'Bearer '+json.token)
localStorage.setItem("user", JSON.stringify(json.user))
})
.catch( err => {
dispatch({
type: SIGN_IN_FAILED, payload: err
})
})
}
With the following solution one can handle JSON API error, Generic API error and Generic fetch error
fetch("api/v1/demo", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
"data": "demo"
})
})
.then(response => {
if (!response.ok) {
return Promise.reject(response);
}
return response.json();
})
.then(data => {
console.log("Success");
console.log(data);
})
.catch(error => {
if (typeof error.json === "function") {
error.json().then(jsonError => {
console.log("Json error from API");
console.log(jsonError);
}).catch(genericError => {
console.log("Generic error from API");
console.log(error.statusText);
});
} else {
console.log("Fetch error");
console.log(error);
}
});
according to This Article :
Per MDN, the fetch() API only rejects a promise when
“a network
error is encountered, although this usually means permissions issues
or similar.”
Basically fetch() will only reject a promise if the user
is offline, or some unlikely networking error occurs, such a DNS
lookup failure.
then, you can use this part of code to use non-network error handlings and make your code more readable
function handleErrors(response) {
if (!response.ok) throw new Error(response.status);
return response;
}
fetch("API URL")
// handle network err/success
.then(handleErrors)
// use response of network on fetch Promise resolve
.then(response => console.log("ok") )
// handle fetch Promise error
.catch(error => console.log(error) );
In order to extract API message from server in case of some error, you have to use the following idiom (which doesn't lie on the surface though), see link
fetch("http://localhost:8090/test/error", {
method: 'GET',
headers: {
'Accept': 'application/json'
}
})
.then(result => {
//Here body is not ready yet, throw promise
if (!result.ok) throw result;
return result.json();
})
.then(result => {
//Successful request processing
console.log(result);
}).catch(error => {
//Here is still promise
console.log(error);
error.json().then((body) => {
//Here is already the payload from API
console.log(body);
});
})
Verbose - yes!, but does exactly what is needed.
Related
I just don't know what is wrong. I am trying to get a json from an api with fetch in javascript. This is the code:
function get(){
fetch('http://localhost:8082/marca/listar', {
method: 'GET',
headers: {},
mode: 'no-cors', // <---
cache: 'default'
})
.then(Response => { return Response.json() })
.then(data => {
console.log(data.nombre)
});
}
This is the url of the api
And I get the following error:
console message image
Per https://developer.mozilla.org/en-US/docs/Web/API/Body/json have you tried:
response.json().then(data => {
// do something with your data
});
it is because the response you get from the server is an array not JSON object
so treat it as such
fetch('http://localhost:8082/marca/listar',
{ cache: 'default' })
.then(function (response) {
if (response.status !== 200) {
return;
}
response.json().then(function (data) {
data.forEach((element) => {
element.whatYouWant; /* Change (whatYouWant) to your desired output */
})
})
})
.catch(function (err) {
console.log('Fetch Error :-S', err);
});
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
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.
I have to set an header in api call. My POST API calls are working fine. But in my get api calls, header is not getting set.
return fetch('http://api-call.com', {
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
'custom-security':'XXXX',
'Purchase-Code':'XXXXXXX',
'Content-Type':'application/json',
'Cache-Control':'max-age=640000'
}
})
.then((response) => response.json())
.then((responseJson) => {
console.log(responseJson);
})
.catch((error) => {
console.error(error);
});
You should setup a Request object and pass your headers wrapped into a Headers object, like:
var request = new Request('http://api-call.com', {
method: 'GET',
headers: new Headers({
'Accept': 'application/json',
'custom-security':'XXXX',
'Purchase-Code':'XXXXXXX',
'Content-Type':'application/json',
'Cache-Control':'max-age=640000'
})
});
Then just invoke fetch with your request as parameter:
fetch(request)
.then((response) => response.json())
.then((responseJson) => {
console.log(responseJson);
})
.catch((error) => {
console.error(error);
});
Check reference
2021 answer: just in case you land here looking for how to make GET and POST Fetch api requests using async/await or promises as compared to axios.
I'm using jsonplaceholder fake API to demonstrate:
Fetch api GET request using async/await:
const asyncGetCall = async () => {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
const data = await response.json();
// enter you logic when the fetch is successful
console.log(data);
} catch(error) {
// enter your logic for when there is an error (ex. error toast)
console.log(error)
}
}
asyncGetCall()
Fetch api POST request using async/await:
const asyncPostCall = async () => {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
// your expected POST request payload goes here
title: "My post title",
body: "My post content."
})
});
const data = await response.json();
// enter you logic when the fetch is successful
console.log(data);
} catch(error) {
// enter your logic for when there is an error (ex. error toast)
console.log(error)
}
}
asyncPostCall()
GET request using Promises:
fetch('https://jsonplaceholder.typicode.com/posts')
.then(res => res.json())
.then(data => {
// enter you logic when the fetch is successful
console.log(data)
})
.catch(error => {
// enter your logic for when there is an error (ex. error toast)
console.log(error)
})
POST request using Promises:
fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
// your expected POST request payload goes here
title: "My post title",
body: "My post content."
})
})
.then(res => res.json())
.then(data => {
// enter you logic when the fetch is successful
console.log(data)
})
.catch(error => {
// enter your logic for when there is an error (ex. error toast)
console.log(error)
})
GET request using Axios:
const axiosGetCall = async () => {
try {
const { data } = await axios.get('https://jsonplaceholder.typicode.com/posts')
// enter you logic when the fetch is successful
console.log(`data: `, data)
} catch (error) {
// enter your logic for when there is an error (ex. error toast)
console.log(`error: `, error)
}
}
axiosGetCall()
Authenticated POST request using Axios:
const axiosPostCall = async () => {
try {
const { data } = await axios.post('https://jsonplaceholder.typicode.com/posts', {
// your expected POST request payload goes here
title: "My post title",
body: "My post content."
},{
headers: {
Authorization:
`Bearer ${token}`
}
})
// enter you logic when the fetch is successful
console.log(`data: `, data)
} catch (error) {
// enter your logic for when there is an error (ex. error toast)
console.log(`error: `, error)
}
}
axiosPostCall()
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));