This week I have learned to fetch data from an API with javascript and jQuery.
Until now, I've only had to fetch from deeper levels within objects (which I've succeeded at), but I still don't know how to post to specific elements within other objects.
I'm currently working on a smart home project, where I'm the one responsible for the web application.
All device controllers have got a 'favourite' button, which is the one that triggers this function to either favourise or un-favourise the pressed object:
function toggle_favourite(id) {
fetch('../../api/objects?id=' + id)
.then(response => response.json())
.then(data => {
if (data.objects[id-1].favourite == true) {
// set .favourite to 'false'
put(id, {
favourite: false
})
} else {
// set .favourite to 'true'
put(id, {
favourite: true
})
}
})
})
}
function put(id, data) {
fetch('../../api/objects?id='+id, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})
}
The data that I'm trying to change is this favourite value.
How do I manouver over to this 'favourite' value with fech/'PUT'?
If the value isn't top level, you have to fetch the entire object, change the part you want to and then 'PUT'/'POST' the object at the end.
For this example, I fetched the entire object and saved it into a 'const', went through it and changed the 'favourite' value, and at the end I 'PUT' everything back into the object like so:
async function getObject(id){
const response = await fetch('../../api/objects?id='+id)
return response.json()
}
async function saveObject(){
const data = await getObject(int_id)
$.each(data, function(index, objects){
$.each(objects, function(index, values){
if (values.favourite == true ){
values.favourite = false
}
else{
values.favourite = true
}
})
})
put(int_id, data)
function put(id, data) {
fetch('../../api/objects?id='+id, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})
.then(response => response.json())
.then(data => {
console.log('Success:', data);
})
.catch((error) => {
console.error('Error:', error);
})
}
}
Related
I was trying to send data from frontend(React) to backend(express) from a HTML form, and clear the fields after submit. This is what it looks like. Here item and amount are state of controlled components. So my question is as I'm updating state before fetch its not updating it. As fetch and setState are async so which one will get executed first. Even if I write state updates after fetch then also it seems like fetch is executed first. But I'm not getting how ?
function onSub(e, setList, list) {
e.preventDefault();
setList([...list, { item, amount }]);
setItem("");
setAmt("");
fetch("http://localhost:3001/addData", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ title: item, amt: amount }),
})
.then((res) => res.text())
.then((data) => {
console.log(data);
});
}
They're in a race. Normally you'd expect the state to get set first, since the state setters are operating just locally within the DOM and the fetch has to go talk to a server, but they're in a race and you can't count on which one is going to win the race.
If you want to control which happens first, you need to do that explicitly. It's easier to make the fetch finish first since you can just wait to call the state setters until the end of the fetch process. That also gives you the opportunity to handle a fetch failure (transient network error, for instance) without losing the information the user provided because you've already cleared it from state:
function onSub(e, setList, list) {
e.preventDefault();
fetch("http://localhost:3001/addData", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ title: item, amt: amount }),
})
.then((res) => {
if (!res.ok) { // This check was missing
throw new Error(`HTTP error ${res.status}`);
}
return res.text();
})
.then((data) => {
console.log(data);
setList([...list, { item, amount }]);
setItem("");
setAmt("");
})
.catch(error => {
setError(/*...some error message saying something went wrong...*/);
});
}
Side note: I explain the comment about the check that was missing here. To avoid having to write all that boilerplate, I suggest having a few handy utility functions lying around, for instance:
export async function postJSON(url, data) {
const res = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});
if (!res.ok) { // This check was missing
throw new Error(`HTTP error ${res.status}`);
}
return res;
}
Then the code would look like this:
function onSub(e, setList, list) {
e.preventDefault();
postJSON("http://localhost:3001/addData", { title: item, amt: amount })
.then((data) => {
console.log(data);
setList([...list, { item, amount }]);
setItem("");
setAmt("");
})
.catch(error => {
setError(/*...some error message saying something went wrong...*/);
});
}
In my componentDidMount function I call AsyncStorage to get some saved value and then make a GET request and fetch data like below:
componentDidMount() {
AsyncStorage.getItem("token").then(value => {
const url = 'my url';
console.log('token:' + value)
return fetch(url, {
method: 'GET',
headers: new Headers({
'Content-Type': 'application/json',
'token': 'abcd',
'jwt': value
})
})
.then((response) => response.json())
.then((responseJson) => {
this.setState({
dataSource: responseJson,
isLoading: false,
getValue: value
})
})
.catch((Error) => {
console.log(Error)
})
})
}
Now, I need to make another GET request. Suppose if I want to make the same request again in this function , how can I do that?
I solved it very easily from the suggested comments. I did the API call part in two different functions and then called these two functions inside ComponentDidMount like below code-
getFirstApiResposnse() {
AsyncStorage.getItem("token").then(value => {
const url = 'my url';
console.log('token:'+ value)
return fetch(url, {
method: 'GET',
headers: new Headers({
'Content-Type' : 'application/json',
'token': 'abcd',
'jwt': value
})
})
.then((response)=> response.json() )
.then((responseJson) => {
this.setState({
dataSource: responseJson,
isLoading: false,
getValue: value
})
})
.catch((Error) => {
console.log(Error)
});
}
)
};
getSecondApiResponse() {
AsyncStorage.getItem("token").then(value => {
const url = 'my url';
console.log('token:'+ value)
return fetch(url, {
method: 'GET',
headers: new Headers({
'Content-Type' : 'application/json',
'token': 'abcd',
'jwt': value
})
})
.then((response)=> response.json() )
.then((responseJson) => {
console.log('####:'+responseJson.cat_note)
this.setState({
isLoading: false,
getValue: value,
})
})
.catch((Error) => {
console.log(Error)
});
}
)
}
componentDidMount() {
this.getFirstApiResponse();
this.getSecondApiResponse();
}
You can also use Promise.all(). Which comes handy with multiple requests. Also, we can use helper library such as async and use its forEach, waterFall, series, parallel, etc methods depending on project needs. These things make our code more readable and scalable.
I'm calling a function that needs to do 2 things:
Call a parent-level function to update my database using a fetch request and then update my parent-level state
Wait for 1st function to update my state, and then route to a view where my updated data is seen (using props)
The problem is I'm not sure how to wait for this 1st function to be complete before moving on to the 2nd. So when I try to change views with this 2nd function, the data shown isn't updated.
The parent-level function that I would like to run and update my state.
updateEntry = (newHeader, newBody, index) => {
fetch("http://localhost:3000/update", {
method: 'PUT',
body: JSON.stringify({
header: newHeader,
body: newBody,
index: index
}),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => { this.setState({journalDb: data })})
}
The child level function where I call the parent level function using updateEntry()
initiateUpdate = (e) => {
e.preventDefault();
this.props.updateEntry(value1, value2, value3)
this.props.setEditMode();
}
The issue is my parent level state isn't updating in time to show when I change the route using setEditMode()... I'm not sure how to wait for the parent state to update before running this
so what you could do is do something like this
updateEntry = (newHeader, newBody, index) => {
return new Promise((resolve) =>fetch("http://localhost:3000/update", {
method: 'PUT',
body: JSON.stringify({
header: newHeader,
body: newBody,
index: index
}),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => {
this.setState({journalDb: data })
resolve()
})
})
and simply add
initiateUpdate = (e) => {
e.preventDefault();
this.props.updateEntry(value1, value2, value3)
.then(() => this.props.setEditMode());
}
this way you're guaranteed that updateEntry was finished when setEditMode is called
Just change your updateEntry function to return the result of the fetch, as this is a promise. For example:
updateEntry = (newHeader, newBody, index) => fetch("http://localhost:3000/update", {
Then when you call the parent function, treat it just like a promise:
this.props.updateEntry(value1, value2, value3).then(() => {
this.props.setEditMode()
})
I need to return the result of a function from another page in react native which performing a fetch call. I use the method as follows. As I know this is because asynchronous call. Is there a special way to achieve this in react native ?
fetchcall.js
import address from '../actions/address'
const dashboard = {
getvals(){
return fetch(address.dashboardStats(),
{method: "POST",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify( {...
}),
})
.then((response) => response.json())
.then((responseData) => {
console.warn(responseData);
return responseData;
})
.catch((error) => { console.warn(error); })
.done();
// return 'test_val'';
}
}
export default dashboard;
dashboard.js
import dashboard from '../../services/dashboard';
class Dashboard extends Component {
componentDidMount(){
console.warn(dashboard.getvals());
}
}
export default connect(mapStateToProps, bindAction)(Dashboard);
Its display the result as "undefined", but that fetch call works and it displays the result. Any suggestion?
In fetchcall.js you are returning a Promise. Also since you are returning the responseData in the .then() method itself, you don't need the .done() method.
Since getvals() is returning a Promise, you need to access it's value in a .then() method.
Overall, your code should be like this:
function getvals(){
return fetch('https://jsonplaceholder.typicode.com/posts',
{
method: "GET",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
})
.then((response) => response.json())
.then((responseData) => {
console.log(responseData);
return responseData;
})
.catch(error => console.warn(error));
}
getvals().then(response => console.log(response));
The best architectural pattern, I think, is to use a callback function, usually by writing in as an anonymous function.
///Component.js
my_service.login((data=>{
this.setState({body: data.body});
}));
////Service.js
export const login = function (cb){
fetch('http://myapi.com/103?_format=hal_json')
.then((response) =>{
return response.json();
})
.then((data) =>{
cb(data);
});
}
I am still a junior developer, but use this pattern frequently. If someone has a reason for a different approach, I would love to hear it.
fetch always return a Promise doesn't matter what you are returning.
so you can return a string, variable or something else but you have to use .then to access data
file where you make fetch request
export const graphql = () => {
return fetch("https://graphqlzero.almansi.me/api", {
"method": "POST",
"headers": { "content-type": "application/json" },
"body": JSON.stringify({
query: `{
user(id: 1) {
id
name
}
}`
})
})
.then((res) => res.json()).then((responseData) => {
console.log(responseData);
return responseData;
})
}
file where you call function
graphql()
.then((e) => {
console.log("data", e)
});
I'm trying to make a POST request with a GraphQL query, but it's returning the error Must provide query string, even though my request works in PostMan.
Here is how I have it running in PostMan:
And here is the code I'm running in my application:
const url = `http://localhost:3000/graphql`;
return fetch(url, {
method: 'POST',
Accept: 'api_version=2',
'Content-Type': 'application/graphql',
body: `
{
users(name: "Thomas") {
firstName
lastName
}
}
`
})
.then(response => response.json())
.then(data => {
console.log('Here is the data: ', data);
...
});
Any ideas what I'm doing wrong? Is it possible to make it so that the body attribute I'm passing in with the fetch request is formatted as Text like I've specified in the PostMan request's body?
The body is expected to have a query property, containing the query string. Another variable property can be passed as well, to submit GraphQL variables for the query as well.
This should work in your case:
const url = `http://localhost:3000/graphql`;
const query = `
{
users(name: "Thomas") {
firstName
lastName
}
}
`
return fetch(url, {
method: 'POST',
Header: {
'Content-Type': 'application/graphql'
}
body: query
})
.then(response => response.json())
.then(data => {
console.log('Here is the data: ', data);
...
});
This is how to submit GraphQL variables:
const query = `
query movies($first: Int!) {
allMovies(first: $first) {
title
}
}
`
const variables = {
first: 3
}
return fetch('https://api.graph.cool/simple/v1/cixos23120m0n0173veiiwrjr', {
method: 'post',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({query, variables})
})
.then(response => response.json())
.then(data => {
return data
})
.catch((e) => {
console.log(e)
})
I created a complete example on GitHub.