Fetch: POST JSON data
Asked Answered
L

19

1081

I'm trying to POST a JSON object using fetch.

From what I can understand, I need to attach a stringified object to the body of the request, e.g.:

fetch("/echo/json/",
{
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    method: "POST",
    body: JSON.stringify({a: 1, b: 2})
})
.then(function(res){ console.log(res) })
.catch(function(res){ console.log(res) })

When using jsfiddle's JSON echo I'd expect to see the object I've sent ({a: 1, b: 2}) back, but this does not happen - chrome devtools doesn't even show the JSON as part of the request, which means that it's not being sent.

Loraineloralee answered 21/4, 2015 at 14:54 Comment(8)
What browser are you using?Gurglet
@KrzysztofSafjanowski chrome 42, which is meant to have full fetch supportLoraineloralee
check this fiddle jsfiddle.net/abbpbah4/2 what data you're expecting ? because get request of fiddle.jshell.net/echo/json is showing empty object. {}Ingenious
@KaushikKishore edited to clarify expected output. res.json() should return {a: 1, b: 2}.Loraineloralee
@Loraineloralee I called this https://fiddle.jshell.net/echo/json with post data {"a":1,"b":2} content-type: application/json from rest client and I'm getting {} object That indicated that there is no problem in fetch . I think soIngenious
@Loraineloralee jsfiddle.net/abbpbah4/3 in this fiddle I added a debugger. when debugger will hit check the value of res this time your promise object is not there. so this fetch would have success method. that might work. ;)Ingenious
You forgot to include the json property that contains the data you want to send. However, I the body is not being treated correctly anyway. See this fiddle to see that the delay of 5 seconds gets skipped. jsfiddle.net/99arsnkg Also, when you try to add additional headers, they are ignored. This is probably an issue with fetch() itself.Glanders
Bottom line its a pig to use. There should be an overriding Text() property that always works irrelevant of the (bloody) header types.Anaphrodisiac
L
1211

With ES2017 async/await support, this is how to POST a JSON payload:

(async () => {
  const rawResponse = await fetch('https://httpbin.org/post', {
    method: 'POST',
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({a: 1, b: 'Textual content'})
  });
  const content = await rawResponse.json();

  console.log(content);
})();

Can't use ES2017? See @vp_art's answer using promises

The question however is asking for an issue caused by a long since fixed chrome bug.
Original answer follows.

chrome devtools doesn't even show the JSON as part of the request

This is the real issue here, and it's a bug with chrome devtools, fixed in Chrome 46.

That code works fine - it is POSTing the JSON correctly, it just cannot be seen.

I'd expect to see the object I've sent back

that's not working because that is not the correct format for JSfiddle's echo.

The correct code is:

var payload = {
    a: 1,
    b: 2
};

var data = new FormData();
data.append( "json", JSON.stringify( payload ) );

fetch("/echo/json/",
{
    method: "POST",
    body: data
})
.then(function(res){ return res.json(); })
.then(function(data){ alert( JSON.stringify( data ) ) })

For endpoints accepting JSON payloads, the original code is correct

Loraineloralee answered 23/4, 2015 at 12:34 Comment(5)
To be on the safe side, it would be good to confirm res.ok in case the response code is some kind of error. It'd also be good to have a .catch() clause at the end. I realize this is just a sample snippet, but bear these things in mind for real world usage.Rinderpest
Perhaps I'm being redundant but I'd like to point out that it does work successfully with PUT requests as well.Rodomontade
I did not need 'Accept': 'application/json' nor content-type when sending json data.Titan
@Timo: this depends very much on your server; the server is not required to use these fields. But the server might emit different data depending on Accept, and it might require content-type to accept json. Better set both to avoid endless debugging when the server software updates.Paperboy
Love that it is also wrapped by self invoking asynchronous function.Binah
H
355

I think your issue is jsfiddle can process form-urlencoded request only. But correct way to make json request is pass correct json as a body:

fetch('https://httpbin.org/post', {
  method: 'POST',
  headers: {
    'Accept': 'application/json, text/plain, */*',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({a: 7, str: 'Some string: &=&'})
}).then(res => res.json())
  .then(res => console.log(res));
Hanny answered 27/2, 2017 at 18:20 Comment(2)
For those who aren't familiar with arrow functions, you have to return res.json() there, to get the data in the next .then() call.Steelmaker
You don't have to use return if you don't use curly braces.Sclater
O
113

From search engines, I ended up on this topic for non-json posting data with fetch, so thought I would add this.

For non-json you don't have to use form data. You can simply set the Content-Type header to application/x-www-form-urlencoded and use a string:

fetch('url here', {
    method: 'POST',
    headers: {'Content-Type':'application/x-www-form-urlencoded'}, // this line is important, if this content-type is not set it wont work
    body: 'foo=bar&blah=1'
});

An alternative way to build that body string, rather then typing it out as I did above, is to use libraries. For instance the stringify function from query-string or qs packages. So using this it would look like:

import queryString from 'query-string'; // import the queryString class

fetch('url here', {
    method: 'POST',
    headers: {'Content-Type':'application/x-www-form-urlencoded'}, // this line is important, if this content-type is not set it wont work
    body: queryString.stringify({for:'bar', blah:1}) //use the stringify object of the queryString class
});
Ostracod answered 18/2, 2017 at 7:15 Comment(0)
G
56

After spending some times, reverse engineering jsFiddle, trying to generate payload - there is an effect.

Please take eye (care) on line return response.json(); where response is not a response - it is promise.

var json = {
    json: JSON.stringify({
        a: 1,
        b: 2
    }),
    delay: 3
};

fetch('/echo/json/', {
    method: 'post',
    headers: {
        'Accept': 'application/json, text/plain, */*',
        'Content-Type': 'application/json'
    },
    body: 'json=' + encodeURIComponent(JSON.stringify(json.json)) + '&delay=' + json.delay
})
.then(function (response) {
    return response.json();
})
.then(function (result) {
    alert(result);
})
.catch (function (error) {
    console.log('Request failed', error);
});

jsFiddle: http://jsfiddle.net/egxt6cpz/46/ && Firefox > 39 && Chrome > 42

Gurglet answered 21/4, 2015 at 17:7 Comment(3)
yw. Curious detail, it works for me in the old way with fetch (#41985393) instead the application/json. Perhaps you know why ...Sweeps
The Content-Type is application/json, but your actual body appears to be x-www-form-urlencoded - I don't think this should work? If it does work, your server must be pretty forgiving. The answer by @Hanny below appears to be the correct one.Persse
30 minutes wasted trying to convert to json an awaited responseWallack
R
42

2021 answer: just in case you land here looking for how to make GET and POST Fetch api requests using async/await or promises as compared to axios.

I'm using jsonplaceholder fake API to demonstrate:

Fetch api GET request using async/await:

         const asyncGetCall = async () => {
            try {
                const response = await fetch('https://jsonplaceholder.typicode.com/posts');
                 const data = await response.json();
                // enter you logic when the fetch is successful
                 console.log(data);
               } catch(error) {
            // enter your logic for when there is an error (ex. error toast)
                  console.log(error)
                 } 
            }


          asyncGetCall()

Fetch api POST request using async/await:

    const asyncPostCall = async () => {
            try {
                const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
                 method: 'POST',
                 headers: {
                   'Content-Type': 'application/json'
                   },
                   body: JSON.stringify({
             // your expected POST request payload goes here
                     title: "My post title",
                     body: "My post content."
                    })
                 });
                 const data = await response.json();
              // enter you logic when the fetch is successful
                 console.log(data);
               } catch(error) {
             // enter your logic for when there is an error (ex. error toast)

                  console.log(error)
                 } 
            }

asyncPostCall()

GET request using Promises:

  fetch('https://jsonplaceholder.typicode.com/posts')
  .then(res => res.json())
  .then(data => {
   // enter you logic when the fetch is successful
    console.log(data)
  })
  .catch(error => {
    // enter your logic for when there is an error (ex. error toast)
   console.log(error)
  })

POST request using Promises:

fetch('https://jsonplaceholder.typicode.com/posts', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
   body: JSON.stringify({
     // your expected POST request payload goes here
      title: "My post title",
      body: "My post content."
      })
})
  .then(res => res.json())
  .then(data => {
   // enter you logic when the fetch is successful
    console.log(data)
  })
  .catch(error => {
  // enter your logic for when there is an error (ex. error toast)
   console.log(error)
  })  

GET request using Axios:

        const axiosGetCall = async () => {
            try {
              const { data } = await axios.get('https://jsonplaceholder.typicode.com/posts')
    // enter you logic when the fetch is successful
              console.log(`data: `, data)
           
            } catch (error) {
    // enter your logic for when there is an error (ex. error toast)
              console.log(`error: `, error)
            }
          }
    
    axiosGetCall()

POST request using Axios:

const axiosPostCall = async () => {
    try {
      const { data } = await axios.post('https://jsonplaceholder.typicode.com/posts',  {
      // your expected POST request payload goes here
      title: "My post title",
      body: "My post content."
      })
   // enter you logic when the fetch is successful
      console.log(`data: `, data)
   
    } catch (error) {
  // enter your logic for when there is an error (ex. error toast)
      console.log(`error: `, error)
    }
  }


axiosPostCall()
Rebutter answered 16/5, 2021 at 14:52 Comment(0)
C
22

Had the same issue - no body was sent from a client to a server. Adding Content-Type header solved it for me:

var headers = new Headers();

headers.append('Accept', 'application/json'); // This one is enough for GET requests
headers.append('Content-Type', 'application/json'); // This one sends body

return fetch('/some/endpoint', {
    method: 'POST',
    mode: 'same-origin',
    credentials: 'include',
    redirect: 'follow',
    headers: headers,
    body: JSON.stringify({
        name: 'John',
        surname: 'Doe'
    }),
}).then(resp => {
    ...
}).catch(err => {
   ...
})
Cosmos answered 21/10, 2017 at 7:27 Comment(0)
M
20

I have created a thin wrapper around fetch() with many improvements if you are using a purely json REST API:

// Small library to improve on fetch() usage
const api = function(method, url, data, headers = {}){
  return fetch(url, {
    method: method.toUpperCase(),
    body: JSON.stringify(data),  // send it as stringified json
    credentials: api.credentials,  // to keep the session on the request
    headers: Object.assign({}, api.headers, headers)  // extend the headers
  }).then(res => res.ok ? res.json() : Promise.reject(res));
};

// Defaults that can be globally overwritten
api.credentials = 'include';
api.headers = {
  'csrf-token': window.csrf || '',    // only if globally set, otherwise ignored
  'Accept': 'application/json',       // receive json
  'Content-Type': 'application/json'  // send json
};

// Convenient methods
['get', 'post', 'put', 'delete'].forEach(method => {
  api[method] = api.bind(null, method);
});

To use it you have the variable api and 4 methods:

api.get('/todo').then(all => { /* ... */ });

And within an async function:

const all = await api.get('/todo');
// ...

Example with jQuery:

$('.like').on('click', async e => {
  const id = 123;  // Get it however it is better suited

  await api.put(`/like/${id}`, { like: true });

  // Whatever:
  $(e.target).addClass('active dislike').removeClass('like');
});
Maxie answered 17/7, 2017 at 21:41 Comment(0)
P
13

This is related to Content-Type. As you might have noticed from other discussions and answers to this question some people were able to solve it by setting Content-Type: 'application/json'. Unfortunately in my case it didn't work, my POST request was still empty on the server side.

However, if you try with jQuery's $.post() and it's working, the reason is probably because of jQuery using Content-Type: 'x-www-form-urlencoded' instead of application/json.

data = Object.keys(data).map(key => encodeURIComponent(key) + '=' + encodeURIComponent(data[key])).join('&')
fetch('/api/', {
    method: 'post', 
    credentials: "include", 
    body: data, 
    headers: {'Content-Type': 'application/x-www-form-urlencoded'}
})
Pinnace answered 27/7, 2017 at 8:52 Comment(0)
H
9

The top answer doesn't work for PHP7, because it has wrong encoding, but I could figure the right encoding out with the other answers. This code also sends authentication cookies, which you probably want when dealing with e.g. PHP forums:

julia = function(juliacode) {
    fetch('julia.php', {
        method: "POST",
        credentials: "include", // send cookies
        headers: {
            'Accept': 'application/json, text/plain, */*',
            //'Content-Type': 'application/json'
            "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" // otherwise $_POST is empty
        },
        body: "juliacode=" + encodeURIComponent(juliacode)
    })
    .then(function(response) {
        return response.json(); // .text();
    })
    .then(function(myJson) {
        console.log(myJson);
    });
}
Household answered 24/5, 2018 at 4:25 Comment(1)
For PHP you can also use $data = json_decode(file_get_contents('php://input'), true); to get the array from fetch(requestURI, {method:'POST', body: JSON.stringify(object)});Etheridge
J
7
**//POST a request**


const createTodo = async (todo) =>  {
    let options = {
        method: "POST",
        headers: {
            "Content-Type":"application/json",
        },
        body: JSON.stringify(todo)      
    }
    let p = await fetch("https://jsonplaceholder.typicode.com/posts", options);
    let response = await p.json();
    return response;
}

**//GET request**

const getTodo = async (id) => {
    let response = await fetch('https://jsonplaceholder.typicode.com/posts/' + id);
  let r = await response.json();
  return r;
}
const mainFunc = async () => {
    let todo = {
            title: "milan7",
            body: "dai7",
            userID: 101
        }
    let todor = await createTodo(todo);
    console.log(todor);
    console.log(await getTodo(5));
}
mainFunc()
Janeanjaneczka answered 17/10, 2022 at 7:58 Comment(0)
C
4

It might be useful to somebody:

I was having the issue that formdata was not being sent for my request

In my case it was a combination of following headers that were also causing the issue and the wrong Content-Type.

So I was sending these two headers with the request and it wasn't sending the formdata when I removed the headers that worked.

"X-Prototype-Version" : "1.6.1",
"X-Requested-With" : "XMLHttpRequest"

Also as other answers suggest that the Content-Type header needs to be correct.

For my request the correct Content-Type header was:

"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"

So bottom line if your formdata is not being attached to the Request then it could potentially be your headers. Try bringing your headers to a minimum and then try adding them one by one to see if your problem is resolved.

Casie answered 12/3, 2018 at 16:47 Comment(0)
S
4

If your JSON payload contains arrays and nested objects, I would use URLSearchParams and jQuery's param() method.

fetch('/somewhere', {
  method: 'POST',
  body: new URLSearchParams($.param(payload))
})

To your server, this will look like a standard HTML <form> being POSTed.

Sheltonshelty answered 7/9, 2018 at 17:53 Comment(0)
I
3

You could do it even better with await/async.

The parameters of http request:

const _url = 'https://jsonplaceholder.typicode.com/posts';
let _body = JSON.stringify({
  title: 'foo',
  body: 'bar',
  userId: 1,
});
  const _headers = {
  'Content-type': 'application/json; charset=UTF-8',
};
const _options = { method: 'POST', headers: _headers, body: _body };

With clean async/await syntax:

const response = await fetch(_url, _options);
if (response.status >= 200 && response.status <= 204) {
  let data = await response.json();
  console.log(data);
} else {
  console.log(`something wrong, the server code: ${response.status}`);
}

With old fashion fetch().then().then():

fetch(_url, _options)
  .then((res) => res.json())
  .then((json) => console.log(json));
Imminent answered 8/5, 2021 at 15:20 Comment(0)
S
2

I think that, we don't need parse the JSON object into a string, if the remote server accepts json into they request, just run:

const request = await fetch ('/echo/json', {
  headers: {
    'Content-type': 'application/json'
  },
  method: 'POST',
  body: { a: 1, b: 2 }
});

Such as the curl request

curl -v -X POST -H 'Content-Type: application/json' -d '@data.json' '/echo/json'

In case to the remote serve not accept a json file as the body, just send a dataForm:

const data =  new FormData ();
data.append ('a', 1);
data.append ('b', 2);

const request = await fetch ('/echo/form', {
  headers: {
    'Content-type': 'application/x-www-form-urlencoded'
  },
  method: 'POST',
  body: data
});

Such as the curl request

curl -v -X POST -H 'Content-type: application/x-www-form-urlencoded' -d '@data.txt' '/echo/form'
Stood answered 27/3, 2018 at 15:47 Comment(1)
This is blatantly incorrect. It has nothing to do with the server side whether or not you need to stringify your json. That is exactly what your curl command is doing implicitly! If you don't stringify your objects before passing them as the body you will just send "[object Object]" as the body of your request. A simple test in Dev Tools would show you that. Open it up and try this without leaving this tab: a = new FormData(); a.append("foo","bar"); fetch("/foo/bar", { method: 'POST', body: {}, headers: { 'Content-type': 'application/json' } })Rozamond
V
1

You only need to check if response is ok coz the call not returning anything.

var json = {
    json: JSON.stringify({
        a: 1,
        b: 2
    }),
    delay: 3
};

fetch('/echo/json/', {
    method: 'post',
    headers: {
        'Accept': 'application/json, text/plain, */*',
        'Content-Type': 'application/json'
    },
    body: 'json=' + encodeURIComponent(JSON.stringify(json.json)) + '&delay=' + json.delay
})
.then((response) => {if(response.ok){alert("the call works ok")}})
.catch (function (error) {
    console.log('Request failed', error);
});    
Visible answered 10/11, 2020 at 10:2 Comment(0)
H
1
// extend FormData for direct use of js objects
Object.defineProperties(FormData.prototype, { 
    load: {
       value: function (d) {
                   for (var v in d) {
                      this.append(v, typeof d[v] === 'string' ? d[v] : JSON.stringify(d[v]));
                   }
               }
           }
   })

var F = new FormData;
F.load({A:1,B:2});

fetch('url_target?C=3&D=blabla', {
        method: "POST", 
          body: F
     }).then( response_handler )
Healey answered 27/4, 2021 at 9:44 Comment(0)
A
0

you can use fill-fetch, which is an extension of fetch. Simply, you can post data as below:

import { fill } from 'fill-fetch';

const fetcher = fill();

fetcher.config.timeout = 3000;
fetcher.config.maxConcurrence = 10;
fetcher.config.baseURL = 'http://www.github.com';

const res = await fetcher.post('/', { a: 1 }, {
    headers: {
        'bearer': '1234'
    }
});
Aviles answered 25/8, 2020 at 3:34 Comment(0)
G
0
    [HttpPost]
    public IActionResult FetchDemo(int? id)
    {
        return Json(new { DataResult = "Success" });
    }

Using FormData my code works fine in ASP.NET

    var formData = new FormData();

    formData.append('id', 3);


    fetch("/Home/FetchDemo", {

        method: 'post',

        body: formData
    })

    .then(res => res.json())

    .then(res => console.log(res))

    .catch(err => console.log(err));
Gnomic answered 20/3, 2024 at 11:21 Comment(0)
M
-1

The fetch API is a JavaScript function that allows devs to make HTTP requests. You can fetch (or retrieve) data, submit data, update existing data, and more. And all of this comes in a more powerful, flexible, and cleaner package than XMLHttpRequest.

When making these requests, we primarily deal with four types of HTTP methods, although there are several more:

  1. GET: For retrieving data from the server. It doesn't change the server state.
  2. POST: For sending data to the server, typically resulting in a change on the server.
  3. PUT: For replacing a current resource with a new one.
  4. DELETE: For removing a specified resource.

Note that the code samples below use async/await for simplicity. If you don't want to use async/await, you can change these examples to use the .then() pattern.

For a GET request, try this:

async function getData() {
  try {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.log('Error:', error);
  }
}
getData();

In this block, we're retrieving data and making it available under the variable data.

For POSTing JSON data, use this:

async function postData() {
  try {
    const response = await fetch('https://api.example.com/data', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({key: 'value'})
    });
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.log('Error:', error);
  }
}
postData();

This block POSTs JSON data to a resource. You can capture the response JSON as data.

(This was originally posted on my blog: https://7.dev/post-json-fetch-api/)

Mahone answered 20/7, 2023 at 16:44 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.