search(term) {
//Spotify.getAccessToken() will RETURN accessToken from previous method.
const accessToken = Spotify.getAccessToken();
//Remember to RETURN the fetch
return fetch(`https://api.spotify.com/v1/search?type=track&q=${term}`, {
headers: {
'Authorization': `Bearer ${accessToken}`
}
})
.then(response => {
if (response.ok) {
console.log(response);
return response.json;
};
throw new Error('Request failed!');
}, networkError => {
console.log(networkError.message);
})
.then(jsonResponse => {
if (!jsonResponse.tracks) {
return [];
};
return jsonResponse.tracks.items.map(track => ({
id: track.id,
name: track.name,
artists: track.artists[0].name,
album: track.album.name,
uri: track.uri
}));
});
}
In this method, when I send the GET request, the console logs the initial response, but when I check the actual content of the response it is empty and doesn't contain any tracks. Yet, when I type the end point url (specified in fetch()), I can see the results in the browser. I've been trying to find a solution for a few hours but I can't see what I'm doing wrong.
Thanks.
chain a .catch on the promise and console the error, oh and btw you should return a
response.json() like a function from there not just response.json
Related
Oh once again I have those Promise.all blues:( I have a function that makes an array of fetch call's from provided urls and then we want to retrieve data via a Promise.all and return array of reponses or better yet just return the promise to calling function. . The problem is this results in error w/console showing:
There was problem retrieving data. TypeError: r.json is not a function
The code for the function is :
const getLeagueLeaders = (url, params) => {
// First let's create the array of url's
let queryURLs = [];
params.forEach((param) => {
queryURLs.push(
fetch(`${url}${new URLSearchParams(param)}`, {
method: "get",
headers: {
Authorization:
"Basic ==",
},
}).then((res) => res.json())
);
});
return (
Promise.all(queryURLs)
// map array of responses into an array of response.json() to read their content
.then((responses) => responses.map((r) => r.json()))
.catch((err) => {
console.error("There was problem retrieving data.", err);
})
);
};
module.exports = getLeagueLeaders;
And in Vue component
mounted: async function () {
const leagueLeadersResponseArray = await getLeagueLeaders(
this.fetchBaseUrl,
this.params
);
this.qbLeaders =
leagueLeadersResponseArray[0].cumulativeplayerstats.playerstatsentry;
Obviously leagueLeadersResponseArray is undefined. I researched .json() and dont see how I am using it incorrectly. At first i thought I needed a Promise.all wrapper for the responses.map((r) => r.json()) but that did no good either. I looked at this link but I am not using fetch as he is. Any guidance much appreciated....
Updated working code for anybody else:
// ---------- src/js/modules/ ------------------ //
/* jshint ignore:start */
// Make function to retrieve League Leaders in a Category
const getLeagueLeaders = (url, params) => {
// First let's create the array of url's
let queryURLs = [];
params.forEach((param) => {
queryURLs.push(
fetch(`${url}${new URLSearchParams(param)}`, {
method: "get",
headers: {
Authorization:
"Basic ==",
},
}).then((res) => res.json())
);
});
return Promise.all(queryURLs).catch((err) => {
console.error("There was problem retrieving data.", err);
});
};
module.exports = getLeagueLeaders;
Your template string is around the entire fetch when it should only be in the argument to fetch:
params.forEach((param) => {
queryURLs.push(fetch(`${url}${new URLSearchParams(param)}`, {
method: "get",
headers: {
Authorization:
"Basic *****==",
}
}));
});
Then, you have a .then(data => {return data}), which doesn't do anything since the return returns from the then callback, not the function. You should instead return the promise that Promise.all gives you:
return Promise.all(queryURLs)
// map array of responses into an array of response.json() to read their content
.then((responses) => responses.map((r) => r.json())) // Get error There was problem retrieving data. TypeError: r.json is not a function
.catch((err) => {
console.error("There was problem retrieving data.", err);
});
What is the way to take data from getUserConnectRequestData function and pass it to getUserConnectResponseData function ?
as you can see so i try to use then and responseData to for save the data of the getUserConnectRequestData function and than i try pass it into the getUserConnectResponseData function but itd not works .
getUserConnectRequestData().then(() => {
responseData();
});
and this is getUserConnectResponseData function that i want to pass the data from getUserConnectRequestData
export const getUserConnectResponseData = (responseData) => {
return new Promise((resolve, reject) => {
// console.log('THIS IS MY RESPONSE ==============>>>>>>>>>>>', responseData);
try {
fetch(
'https://hghghgghghg3223223',
{
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
Req_Type: responseData.Req_Type,
Language_Code: responseData.Language_Code,
User_ID: responseData.User_ID,
Session_ID: responseData.Session_ID,
Session_Key: responseData.Session_Key,
Client_Type: responseData.Client_Type,
Req_Data: {
Bridge_ID: responseData.Bridge_ID,
},
}),
}
)
.then((response) => response.json())
.then((jsonResponse) => {
resolve(jsonResponse);
});
} catch (error) {
reject(error);
}
});
};
You need to accept the parameter and use it, and call the right function:
getUserConnectRequestData().then((responseData) => {
// −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−^^^^^^^^^^^^
getUserConnectResponseData(responseData);
// −^^^^^^^^^^^^^^^^^^^^^^^^^^−^^^^^^^^^^^^
});
But since getUserConnectResponseData takes just that one parameter you know that the then callback will only be called with that one single argument:
getUserConnectRequestData().then(getUserConnectResponseData);
You also need to handle errors, so:
getUserConnectRequestData()
.then(getUserConnectResponseData)
.catch(error => {
// Handle/report error
});
There are a couple of other things to point out, though:
getUserConnectRequestData is falling prey to a promise anti-pattern: You don't need new Promise when you already have a promise (from fetch) to use.
You need to check for HTTP success before calling .json() on the response. Sadly, fetch only rejects on network errors, not HTTP errors.
Here's an updated version of getUserConnectRequestData:
export const getUserConnectResponseData = (responseData) => {
return fetch('https://hghghgghghg3223223', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
Req_Type: responseData.Req_Type,
Language_Code: responseData.Language_Code,
User_ID: responseData.User_ID,
Session_ID: responseData.Session_ID,
Session_Key: responseData.Session_Key,
Client_Type: responseData.Client_Type,
Req_Data: {
Bridge_ID: responseData.Bridge_ID,
},
}),
})
.then((response) => {
if (!response.ok) {
throw new Error("HTTP error " + response.status);
}
return response.json();
});
};
Because of that need for the check, I never use fetch directly, I have wrappers to do the check so I don't have to code it Every Single Time.
// General purpose
function fetchGeneral(...args) {
return fetch(...args)
.then((response) => {
if (!response.ok) {
throw new Error("HTTP error " + response.status);
}
return response;
});
}
// JSON
function fetchJSON(...args) {
return fetch(...args)
.then((response) => {
if (!response.ok) {
throw new Error("HTTP error " + response.status);
}
return response.json();
});
}
Those reject on both network and HTTP errors.
I have a fetch API call that calls API back end and in return, I will get the response object with status code. What I am trying to do is base on return, I wanted to return the JSON response with status code. so that other part of the javascript can manipulate base on status code. My fetch function is as follow.
I have tried with as follow below, but it returns as a given screenshot. It gives me promise value which I didn't want to get.
export const createUser = ( posts ) => {
const apiRoute= "/api/register";
return window.fetch(`${apiRoute}`, {
"headers": headers,
method: "POST",
body: JSON.stringify(posts)
}).then(response => ({
'status' : response.status,
'data' : response.json()
}))
.catch(error => console.error('Error: ', error))
;
}
I know that it might be the duplicate from this post (Fetch api - getting json body in both then and catch blocks for separate status codes), but I do not want my data to return as a promise. Instead, I wanted to return fully constructed well form JSON data.
Something like this.
{status: 400, data: {id:1,name:Hello World}}
how can i achieve this?
"It gives me promise value"
That's right, as per the documentation.
You need to resolve that promise before resolving the outer promise.
For example
.then(response => {
return response.json().then(data => ({
status: response.status,
data
}))
})
Alternatively, use an async function
export const createUser = async ( posts ) => {
const apiRoute= "/api/register";
try {
const response = await window.fetch(apiRoute, {
headers,
method: "POST",
body: JSON.stringify(posts)
})
return {
status: response.status,
data: await response.json()
}
} catch (error) {
console.error('Error: ', error)
throw error
}
}
I have a problem solving this react.js
loadFromServer(pageSize) {
fetch('http://localhost:8080/api/employees')
.then(response => {
return fetch('http://localhost:8080/api/profile/employees',
{
headers: new Headers({
'Accept': 'application/schema+json'
})
}).then(schema => {
this.scheme = schema;
return response.json();
}
)
})
.then(response =>
this.setState(
{
employees: response._embedded.employees,
attributes: Object.keys(this.scheme.json().properties),
pageSize: pageSize,
links: response._links}
)
);
}
at this part
attributes: Object.keys(this.scheme.json().properties),
always return (in promise) TypeError: Cannot convert undefined or null to object.
If I put console.log(this.scheme.json()) I can see the Promise but, why inside setState I get null object?
There are a few issues here:
The main one is that this.schema.json() returns a promise (as you know from your console.log). Promises don't have a properties property, so you're passing undefined to Object.keys, which then gives you that error.
You're also not checking for errors from fetch, in two different ways: You're not checking .ok (which is such a common error I've posted about it on my anemic little blog), and you're not checking for promise rejection.
You're also doing some unnecessary promise nesting and could be overlapping your fetch calls more.
First, since it seems you often fetch JSON, I'd suggest giving yourself a utility function for it:
function fetchJSON(...args) {
return fetch(...args)
.then(response => {
if (!response.ok) {
throw new Error('HTTP error ' + response.status);
}
return response.json();
});
}
Notice the .ok check.
Then, also in the "break the problem into smaller pieces" category, I'd have a fetchSchema function:
function fetchSchema(url) {
return fetchJSON(url, {
headers: new Headers({
'Accept': 'application/schema+json'
})
});
}
Then, loadFromServer can use Promise.all and destructuring to run the operations in parallel:
// (I assume this is in a `class` or object initializer, as it doesn't have `function` in front of it)
loadFromServer(pageSize) {
Promise.all(
fetchJSON('http://localhost:8080/api/employees'),
fetchSchema('http://localhost:8080/api/profile/employees')
)
.then(([empResponse, schema]) => {
this.schema = schema;
this.setState({
employees: empResponse._embedded.employees,
attributes: Object.keys(schema.properties),
pageSize: pageSize,
links: empResponse._links
})
)
.catch(error => {
// Do something with the error
});
}
Note the .catch, since you're not returning the promise from loadFromServer. (If you want to buck errors up the chain, add return in front of Promise.all and move the .catch to calling code.)
Side note: Your code used
this.scheme = schema;
Note that the property on the left is scheme (with a final e) but the variable is schema (with a final a). I think you meant schema and so I've included that change in the above, but if the property is really supposed to be this.scheme, you'll want to adjust that. Or if you don't need that property for anything other than the code in loadFromServer, remove that line entirely.
I think you should use Promise.all to run the two requests in parrallel and then retrieve the two responses (by the way response.json() returns a Promise, that's why you have an error in your code) :
loadFromServer(pageSize) {
Promise.all([
fetch('http://localhost:8080/api/employees')
.then(response => {
if (!response.ok) throw Error(response.statusText);
return response.json();
),
fetch('http://localhost:8080/api/profile/employees')
.then(response => {
if (!response.ok) throw Error(response.statusText);
return response.json();
),
]).then(responses => {
this.setState({
employees: responses[0]._embedded.employees,
attributes: Object.keys(responses[1].properties),
pageSize: pageSize,
links: responses[0]._links
})
}).catch(error => {...})
}
I think you need something like:
loadFromServer(pageSize) {
fetch('http://localhost:8080/api/employees')
.then(response => {
return fetch('http://localhost:8080/api/profile/employees', {
headers: new Headers({
'Accept': 'application/schema+json'
})
}).then(schema => {
schema.json().then(data => {
this.scheme = data
})
});
return response.json();
})
.then(response =>
this.setState({
employees: response._embedded.employees,
attributes: Object.keys(this.scheme.properties),
pageSize: pageSize,
links: response._links
})
);
}
Response json() method in Fetch API returns a promise. For this reason fetch requests should be consistently chained with .then(response => response.json()) to get a plain object.
Flattening promises may result in more reliable control flow. Since responses from both requests are used, this would require to either nest then callbacks or passing another response through then chain. async may be useful because it conveniently solves flattening problem:
async loadFromServer(pageSize) {
const employeesResponse = await fetch('http://localhost:8080/api/employees', {
headers: new Headers({ 'Accept': 'application/schema+json' })
});
const employees = await employeesResponse.json();
const schemeResponse = await fetch('http://localhost:8080/api/profile/employees', {
headers: new Headers({ 'Accept': 'application/schema+json' })
});
const scheme = await schemeResponse.json();
this.setState({
employees: employees._embedded.employees,
attributes: Object.keys(scheme.properties),
pageSize: pageSize,
links: response._links
});
}
Since requests don't depend on each other, they could be performed in parallel with Promise.all.
async loadFromServer(pageSize) {
const employeesPromise = fetch('http://localhost:8080/api/employees', {
headers: new Headers({ 'Accept': 'application/schema+json' })
})
.then(res => res.json());
const schemePromise = fetch('http://localhost:8080/api/profile/employees', {
headers: new Headers({ 'Accept': 'application/schema+json' })
})
.then(res => res.json());
const [employees, scheme] = await Promise.all([employeesPromise, schemePromise]);
this.setState({
employees: employees._embedded.employees,
attributes: Object.keys(scheme.properties),
pageSize: pageSize,
links: response._links
});
}
I am working on Reactjs redux on front-end and Rails API as a back-end.
So now I call API with Fetch API method but the problem is I cannot get readable error message like what I got inside the network tabs
this is my function
export function create_user(user,userInfoParams={}) {
return function (dispatch) {
dispatch(update_user(user));
return fetch(deafaultUrl + '/v1/users/',
{
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
method: "POST",
body: JSON.stringify(userInfoParams)
})
.then(function(response) {
console.log(response);
console.log(response.body);
console.log(response.message);
console.log(response.errors);
console.log(response.json());
dispatch(update_errors(response));
if (response.status >= 400) {
throw new Error("Bad response from server");
}
})
.then(function(json){
console.log("succeed json re");
// We can dispatch many times!
// Here, we update the app state with the results of the API call.
dispatch(update_user(json));
});
}
}
But when errors came I cannot figure out how to get readable response message like I got when I check on my browser network tabs
So this is what I got from the network tabs when I got errors.
My console
This is my rails code
def create
user = User.new(user_params)
if user.save
#UserMailer.account_activation(user).deliver_now
render json: user, status: 201
else
render json: { errors: user.errors }, status: 422
end
end
But I cannot find out how can I get that inside my function
Since the text is hidden inside promise within response object, it needs to be handled like a promise to see it.
fetch(bla)
.then(res => {
if(!res.ok) {
return res.text().then(text => { throw new Error(text) })
}
else {
return res.json();
}
})
.catch(err => {
console.log('caught it!',err);
});
Similar to your answer, but with a bit more explanation... I first check if the response is ok, and then generate the error from the response.text() only for the cases that we have a successful response. Thus, network errors (which are not ok) would still generate their own error without being converted to text. Then those errors are caught in the downstream catch.
Here is my solution - I pulled the core fetch function into a wrapper function:
const fetchJSON = (...args) => {
return fetch(...args)
.then(res => {
if(res.ok) {
return res.json()
}
return res.text().then(text => {throw new Error(text)})
})
}
Then when I use it, I define how to handle my response and errors as needed at that time:
fetchJSON(url, options)
.then((json) => {
// do things with the response, like setting state:
this.setState({ something: json })
})
.catch(error => {
// do things with the error, like logging them:
console.error(error)
})
even though this is a bit old question I'm going to chime in.
In the comments above there was this answer:
const fetchJSON = (...args) => {
return fetch(...args)
.then(res => {
if(res.ok) {
return res.json()
}
return res.text().then(text => {throw new Error(text)})
})
}
Sure, you can use it, but there is one important thing to bare in mind. If you return json from the rest api looking as {error: 'Something went wrong'}, the code return res.text().then(text => {throw new Error(text)}) displayed above will certainly work, but the res.text() actually returns the string. Yeah, you guessed it! Not only will the string contain the value but also the key merged together! This leaves you with nothing but to separate it somehow. Yuck!
Therefore, I propose a different solution.
fetch(`backend.com/login`, {
method: 'POST',
body: JSON.stringify({ email, password })
})
.then(response => {
if (response.ok) return response.json();
return response.json().then(response => {throw new Error(response.error)})
})
.then(response => { ...someAdditional code })
.catch(error => reject(error.message))
So let's break the code, the first then in particular.
.then(response => {
if (response.ok) return response.json();
return response.json().then(response => {throw new Error(response.error)})
})
If the response is okay (i.e. the server returns 2xx response), it returns another promise response.json() which is processed subsequently in the next then block.
Otherwise, I will AGAIN invoke response.json() method, but will also provide it with its own then block of code. There I will throw a new error. In this case, the response in the brackets throw new Error(response.error) is a standard javascript object and therefore I'll take the error from it.
As you can see, there is also the catch block of code at the very end, where you process the newly thrown error. (error.message <-- the error is an object consisting of many fields such as name or message. I am not using name in this particular instance. You are bound to have this knowledge anyway)
Tadaaa! Hope it helps!
I've been looking around this problem and has come across this post so thought that my answer would benefit someone in the future.
Have a lovely day!
Marek
If you came to this question while trying to find the issue because response.json() throws "Unexpected token at position..." and you can't find the issue with the JSON, then you can try this, basically getting the text and then parsing it
fetch(URL)
.then(async (response) => {
if (!response.ok) {
const text = await response.text()
throw new Error(text)
}
// Here first we convert the body to text
const text = await response.text()
// You can add a console.log(text), to see the response
// Return the JSON
return JSON.parse(text)
})
.catch((error) => console.log('Error:', error))
.then((response) => console.log(response))
I think you need to do something like this
export function create_user(user,userInfoParams={}) {
return function (dispatch) {
dispatch(update_user(user));
return fetch(deafaultUrl + '/v1/users/',
{
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
method: "POST",
body: JSON.stringify(userInfoParams)
})
.then(function(response) {
console.log(response);
console.log(response.body);
console.log(response.message);
console.log(response.errors);
console.log(response.json());
return response.json();
})
.then(function(object){
if (object.errors) {
dispatch(update_errors(response));
throw new Error(object.errors);
} else {
console.log("succeed json re");
dispatch(update_user(json));
}
})
.catch(function(error){
this.setState({ error })
})
}
}
You can access the error message with this way:
return fetch(deafaultUrl + '/v1/users/',
{
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
method: "POST",
body: JSON.stringify(userInfoParams)
})
.then(function(response) {
console.log(response);
console.log(response.body);
console.log(response.message);
console.log(response.errors);
console.log(response.json());
dispatch(update_errors(response));
if (response.status >= 400) {
throw new Error("Bad response from server");
}
})
.then(function(json){
console.log("succeed json re");
// We can dispatch many times!
// Here, we update the app state with the results of the API call.
dispatch(update_user(json));
})
// here's the way to access the error message
.catch(function(error) {
console.log(error.response.data.message)
})
;
The best choice is not to catch the error in the fetch because this will be useless:
Just in your api put a response with not code error
static GetInvoicesAllData = async (req,res) =>
{
try{
let pool = await new Connection().GetConnection()
let invoiceRepository = new InvoiceRepository(pool);
let result = await invoiceRepository.GetInvoicesAllData();
res.json(result.recordset);
}catch(error){
res.send(error);
}
}
Then you just catch the error like this to show the message in front end.
fetch(process.env.REACT_APP_NodeAPI+'/Invoices/AllData')
.then(respuesta=>respuesta.json())
.then((datosRespuesta)=>{
if(datosRespuesta.originalError== undefined)
{
this.setState({datosCargados:true, facturas:datosRespuesta})
}
else{ alert("Error: " + datosRespuesta.originalError.info.message ) }
})
With this you will get what you want.
You variables coming back are not in response.body or response.message.
You need to check for the errors attribute on the response object.
if(response.errors) {
console.error(response.errors)
}
Check here https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
You should actually be returning an error response code from the server and use the .catch() function of the fetch API
First you need to call json method on your response.
An example:
fetch(`${API_URL}`, {
method: 'post',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(userInfoParams)
})
.then((response) => response.json())
.then((response) => console.log(response))
.catch((err) => {
console.log("error", err)
});
Let me know the console log if it didn't work for you.