# Fetch

The browser's fetch function sends HTTP requests from JavaScript and returns a promise of the response. Chain .then to read the JSON, check response.ok before trusting it, and pass an options object to POST, PUT or DELETE.

Apprentice · JavaScript · REST API · Updated November 3, 2026

## After this lesson you can

- Send a GET request with fetch, and read the JSON from the response with then
- Send POST, PUT and DELETE requests with fetch, with a JSON body and a Content-Type header, and check response.ok

## Key takeaways

- `fetch(url)` sends a `GET` and returns a promise; `.then` says what to do when it's kept, and `response.json()` reads the JSON body.
- `fetch` keeps its promise even for a `404` or a `500`, so check `response.ok` before trusting the response.
- To send data, pass an options object with the `method`, a `Content-Type: application/json` header and `body: JSON.stringify(item)`.
- `.catch` runs when a `then` throws, and when there's no answer at all, such as when the server is down.

In *Calling APIs from C#*, `HttpClient` called your categories API from a C# program. A web page can call it too, from JavaScript, with a function built into every browser: **`fetch`**.

## Putting the page beside the API

A page can call an API freely when both come from the **same origin**: the same address and port. So the simplest setup is to let the API serve the page. Add a `wwwroot` folder to the `NorthwindApi` project from *HTTP and Web APIs*, and two lines to its `Program.cs`, before `app.UseHttpsRedirection();`:

```csharp
app.UseDefaultFiles();
app.UseStaticFiles();
```

`UseStaticFiles` serves the files in `wwwroot`, and `UseDefaultFiles` sends `wwwroot/index.html` when someone asks for `/`. Now `http://localhost:5179/` is your page, and `/api/categories` is your API, on the same origin.

(A page from somewhere else, such as an Angular app on its own port, is blocked by the browser unless the API says that origin is allowed. That's called **CORS**. When you build an Angular app, you'll see a simpler way around it.)

Open the page, press **F12**, and you can run every example in this lesson in the **Console**.

## A GET with fetch

```javascript
fetch('/api/categories/8')
    .then(response => response.json())
    .then(category => console.log(`${category.categoryName}: ${category.description}`));
```

```text
Seafood: Seaweed and fish
```

`fetch('/api/categories/8')` sends a `GET` to the page's own origin. But a request takes time, and JavaScript doesn't stop and wait. Instead, `fetch` returns a **promise**: an object that promises a result later, like a C# `Task`.

**`.then(callback)`** says what to do when the promise is kept. Here there are two steps:

1. The first `then` gets the **response**: status, headers and a body still arriving. **`response.json()`** reads the body and parses the JSON, and it returns another promise.
2. The second `then` gets the parsed **object**, `category`, with the same camelCase names as the JSON.

Each `then` hands its result to the next, so the steps line up in order, like a LINQ chain.

## Checking the response

`fetch` keeps its promise whenever the server **answers**, even with a `404` or a `500`. So check **`response.ok`**, which is `true` for 2xx codes, like `IsSuccessStatusCode`:

```javascript
fetch('/api/categories/300')
    .then(response => {
        console.log(response.status, response.ok);
        if (!response.ok) {
            throw new Error(`The API answered ${response.status}`);
        }
        return response.json();
    })
    .then(category => console.log(category.categoryName))
    .catch(error => console.log(error.message));
```

```text
404 false
The API answered 404
```

- Throwing inside a `then` skips the rest of the chain and jumps to **`.catch(callback)`**, like a `catch` block.
- `.catch` also runs when there's no answer at all: the server is down, or the network failed. Then `fetch` itself breaks its promise, like `HttpRequestException`.

## Sending data: POST, PUT and DELETE

To send anything but a `GET`, give `fetch` a second argument: an **options** object with the `method`, the `headers` and the `body`. The body must be text, so **`JSON.stringify`** turns your object into JSON:

```javascript
const snacks = { categoryName: 'Snacks', description: 'Chips, nuts and pretzels' };

fetch('/api/categories', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(snacks)
})
    .then(response => {
        console.log(response.status);
        return response.json();
    })
    .then(added => console.log(`Added with id ${added.categoryId}`));
```

```text
201
Added with id 9
```

Without the `Content-Type` header, the API wouldn't know the body is JSON, and would answer `415 Unsupported Media Type`.

`PUT` works the same way, and answers with the category as it's now saved, so there's JSON to read, as after a `POST`. A `DELETE` sends nothing back: there's just the status to check.

```javascript
const changed = { categoryId: 9, categoryName: 'Snacks', description: 'Chips, nuts, pretzels and popcorn' };

fetch('/api/categories/9', {
    method: 'PUT',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(changed)
})
    .then(response => {
        console.log(`PUT: ${response.status}`);
        return response.json();
    })
    .then(saved => console.log(`Now: ${saved.description}`))
    .then(() => fetch('/api/categories/9', { method: 'DELETE' }))
    .then(response => console.log(`DELETE: ${response.status}`));
```

```text
PUT: 200
Now: Chips, nuts, pretzels and popcorn
DELETE: 204
```

The first `then` returns `response.json()`'s promise, so the second gets the saved category. The third `then` returns a new `fetch`'s promise, so the `DELETE` waits until the `PUT` is done, and the last `then` gets the `DELETE`'s response.

| To | `fetch` options |
|---|---|
| Read | none: `fetch(url)` is a `GET` |
| Create | `method: 'POST'`, a `Content-Type` header, `body: JSON.stringify(item)` |
| Replace | `method: 'PUT'`, a `Content-Type` header, `body: JSON.stringify(item)` |
| Delete | `method: 'DELETE'` |

## Check yourself

2 questions to try on the lesson page.

## So what

`fetch` is `HttpClient` for the browser: a URL, a method, maybe a JSON body, and a promise of the response. Chains of `then` read nicely for one or two steps; the next lesson shows a way to write longer ones that reads like ordinary code.

## Key terms

- [Fetch API](https://www.ask-codey.com/glossary/fetch-api)
- [Promise](https://www.ask-codey.com/glossary/promise)
- [.then()](https://www.ask-codey.com/glossary/then)
- [.catch()](https://www.ask-codey.com/glossary/catch)
- [Response Object](https://www.ask-codey.com/glossary/response-object)
- [JSON.stringify()](https://www.ask-codey.com/glossary/json-stringify)

## Cheat sheets

- [Fetch](https://www.ask-codey.com/cheat-sheets/calling-apis-from-javascript-fetch-cheat-sheet)

Source: https://www.ask-codey.com/lessons/calling-apis-from-javascript-fetch
