Fetching API POST with Fetch - javascript

I'm working on a project where I have an async function in JS calling to a PHP function, but for some reason it keeps throwing my error function even though I feel I have the syntax correct.
Here is the JS:
async function UpdateBlog() {
var test = "test";
const settings = {
method: 'POST',
body: JSON.stringify({
blog: test
}),
headers: {
'Content-Type': 'application/json',
}
};
var thisurl = baseurl;
const response = await fetch(thisurl, settings);
document.getElementById("scratch").innerHTML = await response.json();
}
And here is what is catching it in PHP
if ($_SERVER["REQUEST_METHOD"] == "POST"){
if(isset($_POST["blog"])){
UpdateBlog($_POST["blog"]);
}
else {
RequestFail();
}
function RequestFail(){
http_response_code(404);
header('Content-Type: application/json');
echo json_encode(false);
}
I have tested the "UpdateBlog" PHP function as a GET and it works, so that's not the issue
Edit: Figured out the first if... statement is being triggered, but it seems that $_POST["blog"] is showing up as empty

If fetch is a "thenable" function, it may be better to use
const result = ''
fetch(thisurl, settings).then(
(result) => {response = result;}
)

Related

Javasript fetch doesnt work on PHP return when $.ajax works

I need to simply retrive data from server trought POST request. I can only edit front-end code. I found out that that jQuery function $.Ajax works and vanila javasript function fetch() doesn't. I figured it out that its due to PHP code doesn't echo message that i want to read but just returns it like this:
<?php
//some php logic
return array(
'result' => 'OK',
'message' => 'data I shall receive',
);
?>
Ajax request returns {result: 'OK', message: 'data I shall receive'} with following code:
this.sendFileToServer = async (file) =>{
let data = new FormData()
data.append('file', file);
let uploadUrl = this.element.getAttribute('data-uploadurl');
$.ajax({
url: uploadUrl,
body:data,
success: function (msg){
console.log(msg);
}
})
}
Javascript fetch request returns error 500 with following code:
this.sendFileToServer = async (file) =>{
let data = new FormData()
data.append('file', file);
let uploadUrl = this.element.getAttribute('data-uploadurl');
const response = await fetch(uploadUrl, {
method: 'POST',
body: data,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
}).then(response => {
console.log(response.text()); //I tried here return diffrent data like response.json(), but it doesn't work
});
}
I know i could use Ajax function when it works, but i want my code to be jQuery free. I don't understand why the fetch function doesn't work when it should be the same request. Any help how to make it work or Explanation why i can't work would be nice.

Cloudflare Workers FETCH POST Type Error Failed to execute function

Multiple people have brought up issues similar to mine in this community and cloudflare's community. It still seems largely unsolved so I’m asking in hopes of a solution.
I’m trying to create a feature for users to sign up through mailchimp. User info goes from browser to workers to mail chimp. I’m getting the following errors:
TypeError: Failed to execute function: parameter 1 is not of type
‘Response’. at line 0, col -2
Request.Body is not being read
Request from Client:
const response = await axios({
method: "post",
url: "http://127.0.0.1:8787/signup",
data: {
MERGE0: email,
MERGE1: firstName,
MERGE2: lastName,
},
headers: {
"Content-Type": "application/json",
}
});
Workers part 1 (function to read request body):
https://developers.cloudflare.com/workers/examples/read-post
async function readRequestBody(request) {
const { headers } = request
const contentType = headers.get('content-type') || ''
if (contentType.includes('application/json')) {
return JSON.stringify(await request.json())
} else if (contentType.includes('application/text')) {
return request.text()
} else if (contentType.includes('text/html')) {
return request.text()
} else if (contentType.includes('form')) {
const formData = await request.formData()
const body = {}
for (const entry of formData.entries()) {
body[entry[0]] = entry[1]
}
return JSON.stringify(body)
} else {
// Perhaps some other type of data was submitted in the form
// like an image, or some other binary data.
return 'a file'
}
}
Workers part 2 (to Post JSON File to Mail Chimp):
https://developers.cloudflare.com/workers/examples/post-json
async function gatherResponse(response) {
const { headers } = response
const contentType = headers.get('content-type') || ''
if (contentType.includes('application/json')) {
return JSON.stringify(await response.json())
} else if (contentType.includes('application/text')) {
return response.text()
} else if (contentType.includes('text/html')) {
return response.text()
} else {
return response.text()
}
}
Workers Part 3 (to Handle Post Request):
async function eventHandler(request) {
const pathname = request.url
try {
if (pathname.indexOf('signup') !== -1) {
const reqBody = await readRequestBody(request)
const { MERGE0, MERGE1, MERGE2 } = reqBody
// Construct req data
const data = {
members: [
{
email_address: MERGE0,
status: 'subscribed',
merge_fields: {
FNAME: MERGE1,
LNAME: MERGE2,
},
},
],
}
const postData = JSON.stringify(data)
const options = {
method: 'POST',
headers: {
Authorization: `auth ${MAILCHIMP_API_KEY}`,
},
body: postData,
}
const url = `https://us5.api.mailchimp.com/3.0/lists/${MAILCHIMP_AUDIENCE_ID}`
const res = await fetch(url, options)
const results = await gatherResponse(res)
return results
}
} catch (err) {
console.log(err)
}
}
addEventListener('fetch', event => {
event.respondWith(eventHandler(event.request))
})
A few other posts I’ve referenced:
https://community.cloudflare.com/t/fetch-with-post-method-ignores-body/147758/3
https://community.cloudflare.com/t/how-to-post-with-a-body-as-readable-stream/211335
https://community.cloudflare.com/t/using-get-fetch-for-api-javascript-worker/98297
You have this code:
event.respondWith(eventHandler(event.request))
The event.respondWith() function needs to take a Response object as its parameter. However, your eventHandler() function does not return a Response. In some cases, the function does not return a result at all, and in other cases, it returns a string.
In cases where you don't want to modify the request/response, you can have eventHandler pass through the request to origin like so:
return fetch(request);
In cases where you have received a response from the origin, and you want to return it directly to the client unmodified, you should just return repsonse instead of return response.text().
In cases where you have created some new response text that you want to return, you need to wrap it in a Response, like:
return new Respnose(text, {headers: {"Content-Type": "text/plain"}});

Why I cant pass an array of objects as a URL parameter from JavaScript to PHP using window.fetch?

It is very strange that I couldn't find some posts related to this problem.
I have an array of objects and I want to pass it as parameter to PHP using window.fetch function:
var stringified_obj = JSON.stringify(trafficFilterHolder)
window.fetch('https://127.0.0.1/filters.php?filt=' + stringified_obj, { credentials: 'include' })
.then(function (data) {
//
}).catch(function (err) {
//
});
This is my PHP (simplified):
$trafficFilters = $_GET["filt"];
$JSON = json_encode($trafficFilters);
echo $JSON;
The response I get is:
Even if I try to return a simple message "Hello", i dont receive it back in JS:
echo "Hello";
I was doing something wrong + #Sammitch helped with encoding so here is the solution:
var stringified_obj = JSON.stringify(trafficFilterHolder)
window.fetch('https://127.0.0.1/filters.php?filt=' + encodeURIComponent(stringified_obj), { credentials: 'include' })
.then(function (response) {
return response.json(); //I added this callback to get the response
}).then(function (data) {
//
}).catch(function (err) {
//
});

Using Return Value from Async Function In IF Statement

New to NodeJS and JavaScript
I am using NodeJS to make an API call to return some JSON data within an async function. Presently, the API call is working as intended and I have parsed the data that I am looking for. The trouble I am having is using that parsed json data as a condition within an IF statement so I can continue along with the rest of the scripts intended functions. To simplify for the mean time I have written it to display a string statement if the JSON data is what I expect it to be:
const fetch = require("node-fetch");
var accessToken = "Bearer <ACCESS TOKEN>";
var url = '<API ENDPOINT>';
var headers = {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': accessToken
};
const getData = async url => {
try {
const response = await fetch(url, {method: "Get", headers: headers});
const json = await response.json();
console.log(json.status);
return json.status
} catch (error) {
console.log(error);
}
};
let apiStatus = getData(url);
let activated = "ACTIVATED";
let configured = "CONFIGURED";
if (apiStatus.toString() !== activated) {
console.log("BLAH");
}
Essentially, if the return value of "json.status" is equal to "ACTIVATED", then I will perform an action. Else, I will perform a different action.
At present, I am unable to use the output of the "getData()" to be used in an IF condition. Any assistance with next steps would be appreciated.
You need to wait for the promise to be resolved, because right now you'd be evaluating a Promise (pending) object.
Your getData() function is fine.
let apiStatus = await getData(url);
I don't think async works on the window scope, you can try this out. If this doesn't work, then you just need to wait for the promise to be resolved.
getData(url).then(
status => {
if (status.toString() === 'activated'){
...
}
});

Dynamically decide which Fetch response method to use

I work with a Backend API which returns different data types for different requests to the same endpoint. While a more appropriate solution would be to unify the data type returned, legacy, time and lack of tests play against this solution.
I am centralizing my call method to be used by other parts of the application which need to call the endpoint. This call method implements fetch. For information:
export default function call<P> (method: TCallMethod, payload: P, parameter?: string): Promise<IServerResponseObject> {
const url: string = buildUrl(parameter);
const body: string | null = payload ? JSON.stringify(payload) : null;
return fetch(url, {
method,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: `Bearer ${getAuthToken()}`
},
body
}).then(async (response) => {
let body: IServerResponseObjectBody = {
message: '',
code: ''
};
if (response) {
body = await response.json();
}
return {
status: response.status,
body
};
});
}
As I receive data, I am using the Response.json method to decode it.
if (response) {
body = await response.json();
}
The problem is that sometimes I receive no data (when the user is not authenticated - although that's an edge case) or the server responds with just a boolean.
In that case, the json() execution fails, because we are not handling JSON data.
ie:
FetchError: invalid json response body at http://localhost:4545/api/definition/isNameUnique/used%20name reason: Unexpected end of JSON input
I am wondering if there is a cleaner way than nesting try/catches to determine which decode method to use from the ones available: https://developer.mozilla.org/en-US/docs/Web/API/Body#Methods
This feels like a potential solution: https://developer.mozilla.org/en-US/docs/Web/API/Body#Properties but the documentation is not too explicit and lacks examples on how to use it.
It sounds to me like you want to use text to read the response, then look at the resulting text and decide what to do. Roughly:
const text = await response.text();
if (!text) {
// no response, act accordingly
} else if (reBool.test(text)) {
// boolean response, determine whether it's true or false and act on it
} else {
// JSON response, parse it
data = JSON.parse(text);
// ...then use it
}
...where reBool is a regular expression to test for the boolean the server sometimes returns, for instance /^(?:true|false)$/i.
If the response may have whitespace, you might trim the result of response.text().
There are some unrelated things you might also want to do:
You're not checking for a successful response (this is a mistake a lot of people make, so many I wrote it up on my otherwise-anemic little blog). Check response.ok before using json or text, etc.
It doesn't make much sense to pass an async function into then as a callback. If you're going to go async, do it earlier, by making call an async function, then work with await throughout the body rather than mixing your metaphors...
Addressing those and folding in the main answer above (you'll need to adjust as necessary, either IServerResponseObject needs changing or you need to do something different with boolean responses):
const reBool = /^(?:true|false)$/i;
export default async function call<P> (method: TCallMethod, payload: P, parameter?: string): Promise<IServerResponseObject> {
const url: string = buildUrl(parameter);
const body: string | null = payload ? JSON.stringify(payload) : null;
const response = await fetch(url, {
method,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: `Bearer ${getAuthToken()}`
},
body
});
const {status} = response;
if (!response.ok) {
throw new Error("HTTP error " + status); // Or `return {status};` or similar, but making it an error is useful
}
const text = (await response.text()).trim();
let result = {status};
if (!text) {
// blank, act accordingly, perhaps:
result.body = null;
} else if (reBool.test(text)) {
result.body = text === "true";
} else {
result.body = JSON.parse(text);
}
return result;
}

Categories