2

An ajax function from jQuery

$.ajax({
  method: "POST",
  url: "some.php",
  data: { name: "John", location: "Boston" }
})
  .done(function( msg ) {
    console.log( "Data Saved: " + msg );
  });

And this is a request using a fetch API

const data = { name: "John", data: "Boston" };
const options = {
    method: 'POST',
    headers: {
              'Content-Type': 'application/json',
             },
    body: data,
};

const response = await fetch('/api ',options);
const responseData = await response.json();
console.log(responseData);

Also how come this fetch implementation is producing an error in my node terminal ?
If I use a digit instead of 'Boston' for example the Unexpected token changes to '<' .

SyntaxError: Unexpected token o in JSON at position 1
    at JSON.parse (<anonymous>)

Is there anything I should watch out for between these two?
data of ajax and body of fetch?

(I am not using them simultaneously the way)

Paul Omega
  • 35
  • 7
  • 1
    At least strongly related: https://stackoverflow.com/questions/65481693/how-do-i-get-fetch-to-post-data-the-same-way-as-jquery-does – T.J. Crowder Dec 29 '20 at 14:46

2 Answers2

3
'Content-Type': 'application/json',

You claim you are sending JSON.

const data = { name: "John", data: "Boston" };

body: data,

data is an object, not JSON.

When it gets forced into a string (because it isn't a data type recognised by fetch so it gets converted to a string automatically) it becomes "[object Object]" which is still not JSON. (The [ starts an array and then the o is an error).


If you want to send JSON, you need to convert to the object to JSON yourself. Use JSON.stringify.


Also note that while the server-side code appears to be able to handle JSON input, jQuery sends application/x-www-form-urlencoded data, not JSON.

So to match that you would need:

var searchParams = new URLSearchParams();
searchParams.append("name", "John");  
searchParams.append("location", "Boston");

const options = {
    method: 'POST',
    headers: {
              'Content-Type': 'application/x-www-form-urlencoded',
             },
    body: searchParams.toString(),
};

const response = await fetch('/api ',options);
const responseData = await response.json();
console.log(responseData);
Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
3

From the documentation we can see that...

When data is an object, jQuery generates the data string from the object's key/value pairs unless the processData option is set to false. For example, { a: "bc", d: "e,f" } is converted to the string "a=bc&d=e%2Cf". If the value is an array, jQuery serializes multiple values with same key based on the value of the traditional setting (described below). For example, { a: [1,2] } becomes the string "a%5B%5D=1&a%5B%5D=2" with the default traditional: false setting.

fetch doesn't do that. But separately, you've said you're sending JSON by including Content-Type: application/json as a header, but you aren't doing that (and your jQuery code doesn't do that either, it sends URI-encoded data).

You have to do it yourself. If you want to send JSON, use JSON.stringify:

const options = {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
    },
    body: JSON.stringify(data),
};

If you want to send URI-encoded data, use URLSearchParams:

const data = new URLSearchParams([ // Note this is an array of
    ["name", "John"],              // [name, value] arrays
    ["data", "Boston"],
]);
const options = {
    method: 'POST',
    body: data,
};

If you want to send standard form encoding, use FormData (exactly like the above, but with FormData instead of URLSearchParams.

Also how come this fetch implementation is producing an error in my node terminal ? If I use a digit instead of 'Boston' for example the Unexpected token changes to '<' .

SyntaxError: Unexpected token o in JSON at position 1
   at JSON.parse (<anonymous>)

Because your object is being coerced to a string, and it coerces to "[object Object]", which is invalid JSON as of the o.


Side note: Your fetch code is falling prey to a footgun in the API: fetch only rejects its promise on network error, not on HTTP error. You can't assume that a fulfilled promise from fetch means your request was successful, you have to check:

const response = await fetch('/api ',options);
if (!response.ok) {
    throw new Error("HTTP error " + response.status);
}
const responseData = await response.json();
console.log(responseData);
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • What do you mean by fetch rejects its promise on *network error*? I'm really new to working with web API and http stuff. Also thank you so much for this side note(I should read the documentations more). – Paul Omega Dec 29 '20 at 15:09
  • 2
    @JohnSmith - If `fetch` can't even talk to the other end defined by the URL (can't connect, or gets no response to the request, or gets back a network-level error like a failed SSL negotiation etc.), that's a network error. But if it successfully talks to the other end and the other end sends back an error via HTTP (such as 5xx [server failure], a 404, that sort of thing), `fetch` fulfills its promise with a `Response` object whose `ok` property is `false` and with the relevant HTTP status code in its `status` property. – T.J. Crowder Dec 29 '20 at 15:29