I'm using ReactJS and changing a simple local database setup to a Parse.com class adapting a link saver from this repo: https://github.com/peterjmag/reading-list
I want to switch the fetch call for the native to parse .save()
Can I use the fetch or should I rewrite the function to use the parse.com language?
Adding the link is declared on this
LinkActions.addLink.listen(function (link) {
fetch('//localhost:3001/links/', {
method: 'post',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
url: link.url
})
})
.then(function(response) {
if (response.status >= 400) {
throw new Error('Bad response from server');
LinkActions.addLink.failed(link);
}
return response.json();
}).then(function (newLinkData) {
LinkActions.addLink.completed(link, newLinkData);
});
});
EDIT
The new code looks something like this:
OK. So my new code looks something like this now.
LinkActions.addLink.listen(function(link) {
var LinkListing = Parse.Object.extend("LinkListing");
var linkListing = new LinkListing();
linkListing.save({
url: link.url
}, {
success: function(linkListing) {
// The object was saved successfully.
},
error: function(linkListing, error) {
// The save failed.
// error is a Parse.Error with an error code and message.
}
});
});
Am I missing something?
You can either keep the fetch and use the parse.com REST API, or refactor your code to use the Javascript API, icnluding functions such as save. I recommend refactoring to the Javascript API because it would make your code much shorter and clearer.
For example, two things which definitely shouldn't be your concern are adding headers to every request and comparing every response status to 400.
Related
Background:
The following AJAX code works successfully (ReactJS + JQuery frontend, PHP backend).
const data = {request_type:"load_data", start:1, end:50};
$.ajax({
type: "POST",
url: "http://localhost/site/server.php",
data: data,
dataType: "json",
success: (JSobject) => {
this.setState({arr: JSobject.arr});
}
});
In Chrome Dev Tools server.php's Headers show up as "Form Data" like so:
At the PHP server backend, there's this line of code:
$request_type = $_POST["request_type"];
In an attempt to learn how to do Fetch (I've avoided it so far mostly because I had AJAX boilerplate that worked well), I've been playing around with trying to build a drop-in replacement for the above code.
I tried this:
const data = {request_type:"load_data", start:1, end:50};
fetch("http://localhost/site/server.php", {
method: "POST",
body: data
})
.then((JSobject) => {
this.setState({arr: JSobject.arr});
});
But I get this PHP error:
Notice: Undefined index: request_type in .....server.php
And in Chrome Dev Tools server.php's Headers show up like so:
So, I tried changing the data to JSON.stringify(data) like so:
const data = {request_type:"load_data", start:1, end:50};
fetch("http://localhost/site/server.php", {
method: "POST",
body: JSON.stringify(data)
})
.then((JSobject) => {
this.setState({arr: JSobject.arr});
});
But I still get the exact same PHP error:
Notice: Undefined index: request_type in .....server.php
And in Chrome Dev Tools server.php's Headers show up like so:
Out of general frustration (although it's pointless because I'd be still using JQuery), I thought I'd use JQuery's $.param(), and that would surely work.
So I tried this:
const data = $.param({request_type:"load_data", start:1, end:50});
fetch("http://localhost/site/server.php", {
method: "POST",
body: data
})
.then((JSobject) => {
this.setState({arr: JSobject.arr});
});
Still get the same PHP error
Notice: Undefined index: request_type in .....server.php
And in Chrome Dev Tools server.php's Headers show up like so:
My question: how do I modify the Fetch code above so that it becomes a drop-in replacement for the AJAX code up top.
I realize that the use of horizontal lines can be bothersome for some. You can give yourself permission to believe that it really helps a lot of us regular folks follow what's going on in the question.
Answering my own question for others who might encounter this issue in future (and also for my own reference). Figured out the answer via this link and this link. The following code worked:
fetch("http://localhost/site/server.php", {
method: "POST",
body: JSON.stringify(data)
}).then(response => {
return response.json();
}).then((JSobject) => {
this.setState({arr: JSobject.arr});
});
I had to change some PHP code on the server. The initial "gatekeeper" code was:
$_POST = array_map("trim", $_POST);
$request_type = $_POST["request_type"];
This had to be commented out:
$_POST = array_map("trim", $_POST); //this had to be commented out
And this had to be added instead:
$content = trim(file_get_contents("php://input"));
$_POST = json_decode($content, true);
And in Chrome Dev Tools server.php's Headers show up as "Request Payload":
I also saw some suggestions to add a "headers" key like so:
fetch("http://localhost/site/server.php", {
method: "POST",
headers: {"Content-Type": "application/x-www-form-urlencoded",},
body: JSON.stringify(data)
}).then(response => {
return response.json();
}).then((JSobject) => {
this.setState({arr: JSobject.arr});
});
This did also work, but in Chrome Dev Tools server.php's Headers show up as "Form Data":
Another way to do this is by wrapping the object (to be sent) in JQuery's $.param() (I was interested in this because I often use $.param() to append key-values to form.serialize(), on the client side before sending to the PHP on the server)
const data = $.param({request_type:"load_data", start:1, end:50});
fetch("http://localhost/site/server.php", {
method: "POST",
headers: {"Content-Type": "application/x-www-form-urlencoded",},
body: data
}).then(response => {
return response.json();
}).then((JSobject) => {
this.setState({arr: JSobject.arr});
});
The advantage of doing it this way was that no changes were needed to the code on the server side.
In Chrome Dev Tools server.php's Headers show up as"Form Data":
Without using JQuery:
Re: the previous method (using $.param()), it's possible to do it completely without JQuery, but then one would need some function to chain the javascript object's key-value pairs while properly encoding special characters as x-www-form-urlencoded (this link explains how to do it).
E.g., instead of this:
const data = $.param({request_type:"load_data", start:1, end:50});
...
body: data
One would do this:
const data = {request_type:"load_data", start:1, end:50};
...
...
body: "request_type=load_data&start=1&end=50"
I'd like to make a http request from my cloudcode that gets called on my clientside.
I found this a bit confusing at first so hopefully this helps.
In your Cloud Code main.js
Parse.Cloud.define("POSTfromCloud", function(request, response) {
//runs when Parse.Cloud.run("POSTfromCloud") on the client side is called
Parse.Cloud.httpRequest({
method: "POST",
headers: {
"X-Parse-Application-Id": "[PARSE_APP_ID]",
"X-Parse-REST-API-Key": "[PARSE_REST_ID]",
"Content-Type": "application/json"
},
//adds a new class to my parse data
url: "https://api.parse.com/1/classes/newPOSTfromCloudClass/",
body: {
"newPOSTfromCloudClass": {"key1":"value1","key2":"value2"}
},
success: function (httpResponse) {
console.log(httpResponse.text);
response.success(httpResponse);
},
error:function (httpResponse) {
console.error('Request failed with response code ' + httpResponse.status);
response.error(httpResponse.status);
}
}); //end of Parse.Cloud.httpRequest()
});
On your client side. This can be placed anywhere in any language, just use the Parse.Cloud.run to call the matching Parse.Cloud.define you placed in the cloud. You use the
Parse.Cloud.run('POSTfromCloud', {}, {
success: function(result) {
console.log("Posted a new Parse Class from Cloud Code Successfully! :"+ JSON.stringify(result))
},
error: function(error) {
console.log("Oops! Couldn't POST from Cloud Code successfully.. :"+ error)
}
});
}
Your Result: Assuming your POSTing
(here if you want to delete this new object your url would append the object id like so /newPOSTfromCloudClass/60j1uyaBt )
Know it doesnt have to be a httpRequst cloud function. You can do "anything" in the define and run functions.
NOTE: Also seen my other related question on passing params in this here
I've seen some examples, read the docs and read other question however I'm still not quite sure where to add/properly add params to be passed between Cloud Code and my Client side correctly.
For Instance, Here I am creating a new class from a httpRequest in my Cloud Code
In my Cloud Code main.js
Parse.Cloud.define("POSTfromCloud", function(request, response) {
//runs when Parse.Cloud.run("POSTfromCloud") on the client side is called
Parse.Cloud.httpRequest({
method: "POST",
headers: {
"X-Parse-Application-Id": "[PARSE_APP_ID]",
"X-Parse-REST-API-Key": "[PARSE_REST_ID]",
"Content-Type": "application/json"
},
//adds a new class to my parse data
url: "https://api.parse.com/1/classes/newPOSTfromCloudClass/",
body: {
"newPOSTfromCloudClass": {"key1":"value1","key2":"value2"}
},
success: function (httpResponse) {
console.log(httpResponse.text);
response.success(httpResponse);
},
error:function (httpResponse) {
console.error('Request failed with response code ' + httpResponse.status);
response.error(httpResponse.status);
}
}); //end of Parse.Cloud.httpRequest()
});
on my client side
Parse.Cloud.run('POSTfromCloud', {}, {
success: function(result) {
console.log("Posted a new Parse Class from Cloud Code Successfully! :"+ JSON.stringify(result))
},
error: function(error) {
console.log("Oops! Couldn't POST from Cloud Code successfully.. :"+ error)
}
});
}
My Result:
Bam! Got that working correctly. Now lets say I want to make my url one of many parameters passed how do I do this?
As I was asking this I was also tinkering with some things because I coundn't get anything to pass correctly (or it would return as an empty value) so here I have and example on how I can pass parameters into this.
in my cloudcode main.js
Parse.Cloud.define("POSTfromCloud", function(request, response) {
//HERE- make a new instance of 'myValue' for Cloudcode to handle
var myValue = request.params.myValue;
Parse.Cloud.httpRequest({
method: "POST",
....[blah blah]
//AND HERE- placed that here in my body, **note:** you shouldnt store tokens like this, ignore what I named it
body: {
"newPOSTfromCloudClass": {"yourToken":myValue,"key2":"value2"}
},
client side
var myvalue = "I'm The VALUE";
Parse.Cloud.run('POSTfromCloud', {myValue: myvalue}, {
success: function(result) {
Result: this should have passed the param correctly. Again ignore me using the title "yourToken", you shouldn't be storing tokens like that.
This took a while to put together, I hope this can help someone.
I'm trying to extract some projects from the Asana api with vue-resource (https://github.com/vuejs/vue-resource), a Vue.js add-on that makes ajax calls simple. I'm using an api key to access Asana, but I can't figure out how to pass the key in the request header using vue-resource.
In jQuery this works, using beforeSend:
$.ajax ({
type: "GET",
url: "https://app.asana.com/api/1.0/projects?opt_fields=name,notes",
dataType: 'json',
beforeSend: function(xhr) {
xhr.setRequestHeader("Authorization", "Basic " + "XXXXXX");
},
success: function (data){
// console.log(data);
}
});
Where XXXXXX is the Asana api key + ':' converted with btoa(). https://asana.com/developers/documentation/getting-started/authentication
Without needing to authenticate, the Vue instance should be fine with a simple request in the ready function:
new Vue({
el: '#asana_projects',
data: {
projects : []
},
ready: function() {
this.$http.get('https://app.asana.com/api/1.0/projects?opt_fields=name,notes', function (projects) {
this.$set('projects', projects); // $set sets a property even if it's not declared
});
},
methods: {
// functions here
}
});
This, of course, returns a 401 (Unauthorized), since there is no api key in there.
On the vue-resource github page there is also a beforeSend option for the request, but even though it is described right there I can't seem to figure out the correct syntax for it.
I have tried
this.$http.get( ... ).beforeSend( ... );
// -> "beforeSend is not a function", and
this.$http.get(URL, {beforeSend: function (req, opt) { ... }, function(projects) { //set... });
// -> runs the function but req and opt are undefined (of course)
I realize I'm being less than clever as I fail to understand a syntax that is right there in the documentation, but any and all help would be much appreciated!
Any takers?
Perhaps I'm missing some subtlety but can't you use the options parameter to the $get call to specify the header? From the docs: https://github.com/vuejs/vue-resource#methods
Methods
Vue.http.get(url, [data], [success], [options])
[...]
Options
[...]
headers - Object - Headers object to be sent as HTTP request headers
[...]
So for instance:
this.$http.get(
'https://app.asana.com/api/1.0/projects?opt_fields=name,notes',
function (projects) {
this.$set('projects', projects); // $set sets a property even if it's not declared
},
{
headers: {
"Authorization": "Basic " + "XXXXXX"
}
}
);
You can also configure the auth token for all calls like this:
Vue.http.options.root = '/root';
Vue.http.headers.common['Authorization'] = 'Basic YXBpOnBhc3N3b3Jk';
See the docs
Not sure if I'm doing this wrong but I want to make a cloud code httpRequest to validate URLs. I wanted to use the HEAD method and save on some data traffic but specifying HEAD seems to do exactly the same as a GET... seems like I'm getting the full body as well.
Parse.Cloud.define('validateURL', function(request, response) {
var params = request.params;
var url = params.url;
Parse.Cloud.httpRequest({
url: url,
followRedirects: true,
method: 'HEAD'
}).then(function(httpResponse) {
// Redundant
var httpObject = {
buffer: httpResponse.buffer,
cookies: httpResponse.cookies,
data: httpResponse.data,
headers: httpResponse.headers,
status: httpResponse.status,
text: httpResponse.text
};
response.success(httpObject);
}, function(error) {
response.error(error.message);
});
});
I guess the question is, is this my problem or Parse.com's problem (or both or neither?)
Your httpRequest looks correct. You might want to file a bug report.