How to use fetch to POST form data as JSON to your API
Tags: api, fetchapi, json
Published: 2020-09-14 13:53:44

---
title: How to use fetch to POST form data as JSON to your API
published: true
date: 2020-09-14 13:50:13 UTC
tags: API,FetchAPI,JSON
canonical_url: https://simonplend.com/how-to-use-fetch-to-post-form-data-as-json-to-your-api/
---

If you’re using the browser [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) to make requests from a web page in your front end through to your back end API, at some point you might need to make requests that send data from a form on that page. The good news is that `fetch` supports this, but if you want to send the data to your API as JSON, you’ll need to do a little bit of extra work. Let’s take a look at what’s involved.

## Example HTML form

In the following steps we’re going to create three small chunks of JavaScript that will take any data entered by a user into these form fields and [POST](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST) it to our API as JSON.

```html
<form action="https://jsonplaceholder.typicode.com/users" id="example-form">
	<label for="first-name">
		<strong>First Name:</strong>
		<input type="text" name="first_name" id="first-name">
	</label>

	<label for="last-name">
		Last Name:
		<input type="text" name="last_name" id="last-name">
	</label>

	<input type="submit" value="Create new user">
</form>
```

_![👀](https://s.w.org/images/core/emoji/13.0.0/72x72/1f440.png) It might look like a lot of code in the steps below, but most of it is comments to help you understand what’s happening and why._

## Step 1. Listen for when a user submits the form

A [submit event](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit_event) is dispatched by the browser when the user clicks the form’s submit button or when they are focused on a form field and press the return key on their keyboard.

```javascript
const exampleForm = document.getElementById("example-form");

/**
 * We'll define the `handleFormSubmit()` event handler function in the next step.
 */
exampleForm.addEventListener("submit", handleFormSubmit);
```

## Step 2. Read the values of all the form fields with `FormData`

The [FormData API](https://developer.mozilla.org/en-US/docs/Web/API/FormData) is natively [supported by most modern browsers](https://caniuse.com/mdn-api_formdata) and provides a straightforward way of accessing the values for all the fields in a an HTML form: you pass it a reference to a form element and it will do the rest for you.

```javascript
/**
 * Event handler for a form submit event.
 *
 * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit_event
 * 
 * @param {SubmitEvent} event
 */
async function handleFormSubmit(event) {
	/**
	 * This prevents the default behaviour of the browser submitting
	 * the form so that we can handle things instead.
	 */
	event.preventDefault();

	/**
	 * This gets the element which the event handler was attached to.
	 *
	 * @see https://developer.mozilla.org/en-US/docs/Web/API/Event/currentTarget
	 */
	const form = event.currentTarget;

	/**
	 * This takes the API URL from the form's `action` attribute.
	 */
	const url = form.action;

	try {
		/**
		 * This takes all the fields in the form and makes their values
		 * available through a `FormData` instance.
		 * 
		 * @see https://developer.mozilla.org/en-US/docs/Web/API/FormData
		 */
		const formData = new FormData(form);

		/**
		 * We'll define the `postFormDataAsJson()` function in the next step.
		 */
		const responseData = await postFormDataAsJson({ url, formData });

		/**
		 * Normally you'd want to do something with the response data,
		 * but for this example we'll just log it to the console.
		 */
		console.log({ responseData });

	} catch (error) {
		console.error(error);
	}
}
```

## Step 3. Format the data as JSON and POST it to a URL with fetch

```javascript
/**
 * Helper function for POSTing data as JSON with fetch.
 *
 * @param {Object} options
 * @param {string} options.url - URL to POST data to
 * @param {FormData} options.formData - `FormData` instance
 * @return {Object} - Response body from URL that was POSTed to
 */
async function postFormDataAsJson({ url, formData }) {
	/**
	 * We can't pass the `FormData` instance directly to `fetch`
	 * as that will cause it to automatically format the request
	 * body as "multipart" and set the `Content-Type` request header
	 * to `multipart/form-data`. We want to send the request body
	 * as JSON, so we're converting it to a plain object and then
	 * into a JSON string.
	 * 
	 * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST
	 * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries
	 * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify
	 */
	const plainFormData = Object.fromEntries(formData.entries());
	const formDataJsonString = JSON.stringify(plainFormData);

	const fetchOptions = {
		/**
		 * The default method for a request with fetch is GET,
		 * so we must tell it to use the POST HTTP method.
		 */
		method: "POST",
		/**
		 * These headers will be added to the request and tell
		 * the API that the request body is JSON and that we can
		 * accept JSON responses.
		 */
		headers: {
			"Content-Type": "application/json",
			"Accept": "application/json"
		},
		/**
		 * The body of our POST request is the JSON string that
		 * we created above.
		 */
		body: formDataJsonString,
	};

	const response = await fetch(url, fetchOptions);

	if (!response.ok) {
		const errorMessage = await response.text();
		throw new Error(errorMessage);
	}

	return response.json();
}
```

## Full code example without comments

Here’s all of the JavaScript code from the steps above pulled together without inline comments:

```javascript
/**
 * Helper function for POSTing data as JSON with fetch.
 *
 * @param {Object} options
 * @param {string} options.url - URL to POST data to
 * @param {FormData} options.formData - `FormData` instance
 * @return {Object} - Response body from URL that was POSTed to
 */
async function postFormDataAsJson({ url, formData }) {
	const plainFormData = Object.fromEntries(formData.entries());
	const formDataJsonString = JSON.stringify(plainFormData);

	const fetchOptions = {
		method: "POST",
		headers: {
			"Content-Type": "application/json",
			Accept: "application/json",
		},
		body: formDataJsonString,
	};

	const response = await fetch(url, fetchOptions);

	if (!response.ok) {
		const errorMessage = await response.text();
		throw new Error(errorMessage);
	}

	return response.json();
}

/**
 * Event handler for a form submit event.
 *
 * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit_event
 *
 * @param {SubmitEvent} event
 */
async function handleFormSubmit(event) {
	event.preventDefault();

	const form = event.currentTarget;
	const url = form.action;

	try {
		const formData = new FormData(form);
		const responseData = await postFormDataAsJson({ url, formData });

		console.log({ responseData });
	} catch (error) {
		console.error(error);
	}
}

const exampleForm = document.getElementById("example-form");
exampleForm.addEventListener("submit", handleFormSubmit);
```

## Handling JSON request bodies in an Express based API

If your API is built with [Express](https://expressjs.com/) you’ll want to configure your routes to be able to accept JSON request bodies. You can use the [body-parser](https://github.com/expressjs/body-parser#readme) middleware to handle this for you. It can be configured to parse the JSON request body for POST/PUT/PATCH requests and it will then add it as an object under a `body` property in the `request` object. This means it’s then available to any middleware or route handlers that are run after the `body-parser` middleware as `request.body`.

```javascript
const bodyParser = require("body-parser");

app.use(bodyParser.json());

app.post("/user", (request, response) => {

	/**
	 * In a real application, the data in `request.body` should
	 * be validated before its used for anything e.g. inserting
	 * into a database.
	 */

	const newUser = {
		first_name: request.body.first_name,
		last_name: request.body.last_name
	};

	response.status(201).json(newUser);
});
```