How to POST JSON data in JavaScript

Feb 8, 2023 | Data fetching, Tools

So, you hear it everywhere, but what is a JSON GET or a JSON POST request exactly? How do I post JSON data?

How to fetch JSON file data in JavaScript? In this article we explain what HTTP requests are, how they work, and how to make HTTP requests in JavaScript to fetch data. We will also discuss some common pitfalls causing requests not to work, and we will discuss a number of tools that can be useful when working with HTTP requests.

What is a JSON POST or JSON GET request?

GET and POST requests are a way to send or receive data via the internet. This is often called data fetching. It is the most common way to exchange data between a “client” and a “server”. Here, a “client” is for example a browser, or a mobile application, but it can also be a server.

An HTTP request involves sending a request and receiving a response.

A request consists of:

  • A method (GET to get data, POST to post new data, PUT to update data, PATCH to partially update data, or DELETE to delete data)
  • An url, for example https://dummyjson.com/products/2. The URL consists of a domain (https://dummyjson.com) referring to a server, and an endpoint referring to a specific resource on that server, for example /products/2 or /users.
  • Optional headers, for example Content-Type describing the content type of the data in the body ("text/plain", "application/json", "image/png"), or Authorization to pass an authorization token.
  • A body with data.

A response consists of:

  • Status (200 OK, 404 Not Found, 401 Unauthorized, 403 Forbidden, etc)
  • Optional headers (like the mentioned Content-Type)
  • A body with data. This can be anything: for example an HTML file, a JPG image, or a PDF document. When fetching data from a server, the data is in most cases JSON.

When exchanging data, the body often is JSON data. But it can be anything, for example an HTML file, a JPG image, or a PDF document.

Overview of a GET request

Here is an example of a typical GET request, meant to fetch data. Normally, the request does not contain a body, and the response will contain the requested data. In this case we request information about a product, a phone, with id 2.

HTTP GET request

Overview of a POST request

Here an example of a typical POST request, meant to add or update data on a server. In this case, we add a new product to a list with products. The request contains a body with the new data, and the response may or may not contain data. A response is often handy when the server fills in optional values in the data model.

HTTP POST request

On a side note, it is good to mention GraphQL in this context of POST requests. When using HTTP requests, it can be that you have to make multiple requests to get all the data you need. For example, first you have to fetch a list with posts, and then you have to fetch the details of the authors of those posts. Due to network latency, this can be slow. GraphQL is a query language which works via an HTTP POST request. It allows sending multiple queries and mutations in one request. It gives a lot of flexibility and can improve the performance of your application thanks to reducing the amount of requests.

Fetching data in JavaScript

JavaScript has built-in support to do HTTP requests. You can use the built-in fetch function, or use a more feature rich library built on top of that.

Make a request with plain JavaScript

Since the HTTP protocol is mainstream, most programming languages have built in support for HTTP requests. This article focuses on HTTP requests in JavaScript, which has the HTML5 fetch API.

Here is an example of a GET request to fetch some data. In this case, it fetches fake data of a product with id 2. Note that there is no need to specify the GET method explicitly, since that is the default. And we do not send a request body with the GET request, we only want to receive data. From the response, you can read the status, the content type in the headers, and the response data itself.

// a GET request
const url = 'https://dummyjson.com/products/2'
const response = await fetch(url)

console.log('status:', response.status)
console.log('Content-Type:', response.headers.get('Content-Type'))
console.log('json:', await response.json())
// output:
//   status: 200
//   Content-Type: application/json; charset=utf-8
//   json: {
//      "id": 2,
//      "title": "iPhone X",
//      "price": 899,
//      ...
//   }
Note that there are two asynchronous steps involved before you actually have the data. First you have to send the request and await the response, and second, you have to retrieve the JSON body asynchronously: the await response.json() part, or, if you want to get the data as text: await response.text().

Here is an example of a POST request, which adds a new product to the server. In this case, we send the data of the new product in the request body, along with the content type in the header. To know whether the request was successful, we can check whether the response status is 200 OK. If not, often the response body contains an informative message.

const newProduct = {
  id: 1017,
  description: 'New Phone Z',
  price: 433
}

// a POST request
const response = await fetch('https://dummyjson.com/products/add', {
  method: 'POST',
  headers: {
	'Content-Type': 'application/json; charset=utf-8'
  },
  body: JSON.stringify(newProduct),
})

console.log('status:', response.status)
// output:
//   status: 200

Make a request using a library

In the previous section we saw how to do a GET and POST request with the built-in JavaScript function fetch. In the POST example, you see that some things are getting a bit verbose already: you have to specify the Content-Type, and you will have to stringify your JSON data via JSON.stringify. And for example when needing to specify an authentication token, it gets more verbose. Can’t that be done automatically? The answer is yes. You can write a little wrapper function yourself, or you can use a ready made library such as Axios.

The same POST request with Axios would look like:

import axios from 'axios'

const newProduct = {
  id: 1017,
  description: 'New Phone Z',
  price: 433
}

// a POST request
const response = await axios.post('https://dummyjson.com/products/add', newProduct)

console.log('status:', response.status)
// output:
//   status: 200

This is easier to read, and you can focus on the actual request instead of the details of it.

On a side note: node.js, which is also JavaScript, does not have the same fetch library built in. It has its own https library to do HTTP requests. This is quite a low level library. In node.js you can also use libraries like Axios, and you can use the node-fetch library, which has the same API as JavaScript’s fetch API in the browser.

6 common pitfalls when making HTTP requests

 it can be that your HTTP request does not work at first. Let us go over some of the most common causes for your HTTP request not to work.

1. Forgetting to serialize the body of your request

When posting data using the plain JavaScript fetch function, it is easy to forget to serialize the body:

// WRONG: forget to serialize the request body 
const response = await fetch('https://dummyjson.com/products/add', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json; charset=utf-8'
  },
  body: newProduct,
})

// RIGHT: serialize the request body
const response = await fetch('https://dummyjson.com/products/add', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json; charset=utf-8'
  },
  body: JSON.stringify(newProduct),
})

This will most likely result in a response with a 400 Bad request status. Using TypeScript will most likely help, since TypeScript can warn about the provided body not being a string. Also, using a library like Axios will help, since Axios will automatically stringify the body when needed.

2. Forgetting to specify a Content-Type

In the same category, it is easy to forget to provide a correct Content-Type header specifying that the body contains JSON data. Many backends are forgiving and can continue when this header is missing, others stop there and will return a response with an error status.

In practice, it can be that the error status and message are misleading, so it can take a bit before you realize what’s wrong. For example, if your data contains special unicode characters and you forget to specify charset=utf-8, it can be that the server accepts the data but wrongly decodes these characters. These wrongly decoded characters may cause an issue in a later stage, somewhere else in the system.

// WRONG: forget to specify a Content-Type
const response = await fetch('https://dummyjson.com/products/add', {
  method: 'POST',
  body: JSON.stringify(newProduct),
})

// RIGHT: specify a Content-Type
const response = await fetch('https://dummyjson.com/products/add', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json; charset=utf-8'
  },
  body: JSON.stringify(newProduct),
})

3. Missing authentication information

The GET and POST requests at the start of the article are using a public API. Most API’s however are not public and require authentication. There are many different ways to authenticate and to pass authentication information along with your request. This can differ for every backend.

In general, there are two steps involved. First, you do a request where you pass credentials and that returns you an authentication token. And second, you pass this authentication token along with every request you make. Normally, the Authentication header is used for this. This can look like:

const response = await fetch(url, {
  method: 'POST',
  headers: {
    'Authentication': 'Bearer mYWgioEoRNfbcHq6Fj8Vq2iuuMsTBBMkYjsrfTGK',
    'Content-Type': 'application/json; charset=utf-8'
  },
  body: JSON.stringify(data),
})

It can get cumbersome to specify the Authentication header with every request you make. Again, a library such as Axios can be very helpful: it allows you to specify the authentication information once, and automatically add this with every HTTP request you do.

4. Thinking there is something wrong with your authentication, whereas there actually is an authentication problem.

Yeah, this can be one of these embarrassing moments that you don’t like to talk about. Spending hours figuring out what is wrong with your authentication header, until you realize there is nothing wrong with it and it is a different problem.

Authentication and authorization sound very similar but are two different things:

  • Authentication: verify that a user actually is the one he/she claims to be
  • Authorization: verify that a user is allowed to see or change specific data

When your request results in a response status 403 Forbidden error, you may accidentally think that there is something wrong with your authentication header. However, this is not the case. Status 403 Forbidden means that the user is logged in, but does not have the rights to get or post the data that you’re trying to get. So, the user is authenticated but not authorized. When a user is not authenticated instead, you should get back a response status 401 Unauthorized.

Now, this probably sounds obvious when reading it. But people sometimes mix up these terms in their heads. I’m not sure, it may be an issue with non-native English speakers. To make matters worse, I’ve seen servers that accidentally return a 403 where it should be a 401 or vice versa. That can give quite some confusion.

5. Blocked by a CORS error

CORS issues can be quite annoying. The difficulty is that you cannot solve the issue client side. It requires server side changes, and that may be out of your control. So, what are CORS errors exactly? Let’s explain.

For security reasons, a web application can by default only fetch data from the same domain. For example, if you have a web application hosted at https://myfancyfrontend.com, it can fetch data from this domain only, like https://myfancyfrontend.com/products/2 and https://myfancyfrontend.com/logo.png . However, it can be that you want to fetch data from a different server like https://myfancybackend.com. You may need to fetch data from third party services like social media or a weather API. This is not possible by default due to the security restrictions, but there is a solution for this: Cross Origin Resource Sharing or CORS.

To enable fetching data from a different domain, a different origin, the server that serves the data has to add an HTTP header Access-Control-Allow-Origin containing the client side domain, for example:

Access-Control-Allow-Origin: https://myfancyfrontend.com

The server can judge an incoming request and provide this header when the origin is ok. If the server is ok with any source, it can send a wild card:

Access-Control-Allow-Origin: *
The browser will check this header and throw a CORS error or Network error when not ok.

In short: when you run into a CORS error, you’ll have to make sure the server sends an Access-Control-Allow-Origin header with the response.

6. No internet

It sounds stupid, but it can happen that you yourself, or a user in a production application, has a flaky internet connection. When the web application does not have proper error handling, this may result in vague errors in the application, or in no errors at all. The latter is the worst: you enter a form, click “Save” and assume that your data is saved. If the application silently fails, you will only discover later that the data is gone.

Only one piece of advice here: make sure that your application does not silently eat errors. Always show at least a simple popup message or something so the user is aware that something went wrong. And log errors to the console, so you as a developer can directly see the details of the failed request.

Developer tools to work with HTTP requests

If your HTTP request does not work, how can you debug it? It can be cumbersome to do in the web application itself, because it may require quite some steps to get the application in such a state that you can make the request you want. And it is not so flexible to try out different options.

There are handy tools to work with HTTP requests that make your life as a developer easier. Here a few examples:

  • You can use a REST Client to work with REST API’s. A few examples are: Insomnia, Postman, or the VS Code plugin named REST Client. With these tools, you can build HTTP requests in a visual way and execute them.
  • You can use command line tools such as cURL to make HTTP requests on the command line.
  • You can use JSON Editor Online to inspect JSON data. You can sort and filter JSON data, compare different versions of responses you received, validate your data, etcetera.

Conclusion

In this article, we explored the HTTP protocol to understand JSON POST and JSON GET requests that you can use to interchange data between a client and a server. In a JavaScript web application, you can use the built-in fetch function, or use a more feature rich library like Axios to make requests. We discussed some common pitfalls causing requests not to work, and we discussed a number of tools that can be helpful when working with HTTP requests.