# Sending Data with fetch

To send data to an API, give fetch a second argument with method POST, a Content-Type header that says the body is JSON, and a body made with JSON.stringify; and know that CORS, the API's rule about which sites may call it, decides whether a page can call it at all.

Journeyman · JavaScript · REST API · Web Development · Updated November 1, 2026

## After this lesson you can

- Send JSON to an API with fetch, using method POST, a Content-Type header and JSON.stringify
- Explain what CORS is and why a page can't call every API

## Key takeaways

- fetch's second argument is an object of options. For a POST, it holds the method, the headers and the body.
- The Content-Type header set to application/json tells the server the body is JSON. Without it, many servers ignore what you sent.
- The body must be text, so turn your object into JSON with JSON.stringify. An object sent as it is arrives as "[object Object]".
- A successful POST usually answers 201 Created, and you still check response.ok, the same as for a GET.
- CORS is the API's own rule about which websites may call it from a page. Only the API's owner can change it.

Signing up for a newsletter, posting a comment, saving a high score to an online leaderboard, sending an adoption application: each of those sends data **to** a server instead of getting data from one. You'll write it whenever a form's answers need to go somewhere other than the visitor's own browser, from a class project's guestbook to the checkout page of a real store.

Riverbend Animal Shelter wants visitors to book a visit with a dog they liked in the breed explorer. The shelter's own server isn't built yet, so this lesson sends its visit requests to **JSONPlaceholder**, a free practice API that answers exactly like a real one, but only pretends to save.

## GET asks, POST sends

Reading the shelter's notice board doesn't change anything on it: that's a `GET`. Filling in a visit card and dropping it in the shelter's mailbox adds something new: that's a `POST`, the method from *What a Web API Is* that asks the server to save new data.

A `POST` carries the new data with it, in the request's **body**: the card inside the envelope. And like an envelope, a request can carry labels on the outside, called **headers**, that tell the server what's inside before it opens it.

## The shape of a POST

`fetch` takes a second argument, an object of **options**. For a `GET` you could leave it out; for a `POST` it holds three things:

```javascript
const response = await fetch(url, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(data),
});
```

- **`method: 'POST'`** says what to do. Without it, `fetch` sends a `GET`.
- **`headers`** are the labels. `'Content-Type': 'application/json'` (read "content type: application slash JSON") says "the body is JSON".
- **`body`** is the data itself. It has to be text, so `JSON.stringify` turns your object into JSON, as in *JSON: Data You Can Save and Send*.

## Sending a visit request

Jordan wants to meet Biscuit on Saturday:

```javascript
const visitRequest = {
  dogName: 'Biscuit',
  visitorName: 'Jordan',
  day: 'Saturday',
};

const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(visitRequest),
});

console.log(response.status, response.statusText);
const saved = await response.json();
console.log(saved);
```

```text
201 Created
{ dogName: 'Biscuit', visitorName: 'Jordan', day: 'Saturday', id: 101 }
```

`201 Created` means "saved, and here it is". The server sends the data back with an `id` it gave the new record, the way a real server would, so your page could show *Visit request #101 sent*. JSONPlaceholder answers `101` every time, because it doesn't really save anything: ask it for post 101 afterward and it says `404`. For practice that's ideal: you can send as many requests as you like without filling anyone's database.

**Try it.** Save the code as `visit.js` in your `dog-explorer` folder and run `node visit.js`. Change the dog's name and the day, and run it again.

## Forget a piece, lose the data

Each of the three options does a job, and leaving one out doesn't always give an error. Take out the `headers` line, and JSONPlaceholder no longer knows the body is JSON, so it ignores it:

```javascript
const visitRequest = { dogName: 'Biscuit', visitorName: 'Jordan', day: 'Saturday' };

const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
  method: 'POST',
  body: JSON.stringify(visitRequest),
});

console.log(await response.json());
```

```text
{ id: 101 }
```

The request "worked" (`201`), and Biscuit, Jordan and Saturday were thrown away. Now put the header back and leave out `JSON.stringify`, sending the object as it is: `body: visitRequest`. An object turned into text without `JSON.stringify` becomes `"[object Object]"`, so that's what the server receives, and it answers `500` with the message *"[object Object]" is not valid JSON*.

When a `POST` seems to work but the data comes back missing, check those two lines first: the `Content-Type` header, and `JSON.stringify` around the body.

## Check the answer, too

A `POST` can fail in all the ways a `GET` can, so everything from *When fetch Fails* still applies. Wrapped in a function, with `response.ok` checked:

```javascript
async function requestVisit(visit) {
  const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(visit),
  });
  if (!response.ok) {
    throw new Error(`The visit wasn't saved (status ${response.status})`);
  }
  return response.json();
}

const saved = await requestVisit({ dogName: 'Luna', visitorName: 'Sam', day: 'Sunday' });
console.log(`Visit request #${saved.id}: ${saved.visitorName} meets ${saved.dogName} on ${saved.day}`);
```

```text
Visit request #101: Sam meets Luna on Sunday
```

`return response.json()` hands back the promise from `json()`, and `await requestVisit(...)` waits for it. On a page, this function would run when a form is submitted (*Reading What People Type*), with a loading state on the **Send** button and an error state if it fails, as in *Loading, Empty and Error States*.

## CORS: why a page can't call every API

Some APIs work perfectly in Node and fail on a page, with a red Console message such as *has been blocked by CORS policy*. That's **CORS** (read "cores", for *cross-origin resource sharing*), and it protects people: a browser won't let a page read answers from another website unless that website says it may, so a page you visit can't quietly read your bank's site in the background with your login. The API decides by sending a header with its answer, `Access-Control-Allow-Origin`; the Dog CEO API and JSONPlaceholder send `*` (read "any"), which lets every page call them. If an API doesn't allow your page, nothing in your JavaScript can change that: only the API's owner can, which is why some APIs are meant to be called from a server, never from a page. Node isn't a browser, so CORS doesn't apply there, and that's why the same request can work in Node and fail on a page.

## Check yourself

2 questions to try on the lesson page.

## So what

`method`, `headers` and `body`: with those three options, your pages stop only reading the internet and start writing to it, whether that's a comment, a score or someone's request to meet the dog they've fallen for.

## Key terms

- [POST](https://www.ask-codey.com/glossary/post)
- [Request Body](https://www.ask-codey.com/glossary/request-body-web-development)
- [Header](https://www.ask-codey.com/glossary/header-web-development)
- [Content-Type](https://www.ask-codey.com/glossary/content-type)
- [JSON.stringify()](https://www.ask-codey.com/glossary/json-stringify)
- [CORS](https://www.ask-codey.com/glossary/cors)

## Cheat sheets

- [Sending Data with fetch](https://www.ask-codey.com/cheat-sheets/javascript-fetch-sending-data-cheat-sheet)

## Related

- [Getting Data with fetch](https://www.ask-codey.com/lessons/javascript-fetch-getting-data)
- [JSON: Data You Can Save and Send](https://www.ask-codey.com/lessons/javascript-json-stringify-and-parse)
- [Reading What People Type](https://www.ask-codey.com/lessons/javascript-forms-reading-values)

Source: https://www.ask-codey.com/lessons/javascript-fetch-sending-data
