Skip to main content

JavaScript Asynchronous

JavaScript Fetch API

Written by Published

fetch requests data from a server and returns a promise.

The examples below define a small stand-in for fetch so they run without a server.

It behaves like the real thing: it returns a promise for a response object.

Example

Example

javascript

// A stand-in for the network, so this example runs on its own.
function fetch(url) {
  return Promise.resolve({
    ok: true,
    status: 200,
    json: function () {
      return Promise.resolve({ name: "Ada" });
    }
  });
}

fetch("/api/user")
  .then(function (response) {
    return response.json();
  })
  .then(function (data) {
    console.log(data.name);
  });

The output is Ada.

Against a real server the only difference is where the data comes from.

How fetch Works

fetch returns a promise for a response object.

The response is not the data - response.json() returns another promise for that.

That is why the classic fetch chain has two then steps.

Syntax

Syntax

javascript

fetch(url)
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.log(error));

Two promises: one for the response, one for the body.

With async and await

The same request reads more directly with await.

Example

Example

javascript

function fetch(url) {
  return Promise.resolve({
    ok: true,
    json: function () { return Promise.resolve({ name: "Grace" }); }
  });
}

async function load() {
  const response = await fetch("/api/user");
  const data = await response.json();
  console.log(data.name);
}

load();

Two awaits, matching the two promises.

fetch Does Not Reject on 404

This surprises almost everyone.

A 404 or 500 is still a successful request, so you must check response.ok.

Example

Example

javascript

function fetch(url) {
  return Promise.resolve({ ok: false, status: 404 });
}

async function load() {
  const response = await fetch("/api/missing");
  if (!response.ok) {
    console.log("request failed with status " + response.status);
    return;
  }
  console.log("got the data");
}

load();

Only a network failure rejects the promise.

Handling Both Kinds of Failure

A complete request checks the status and catches network errors.

Example

Example

javascript

function fetch(url) {
  return Promise.reject(new Error("network down"));
}

async function load() {
  try {
    const response = await fetch("/api/user");
    if (!response.ok) throw new Error("status " + response.status);
    console.log("loaded");
  } catch (error) {
    console.log("failed: " + error.message);
  }
}

load();

The output is failed: network down.

Sending Data

A second argument sets the method, headers and body.

The body must be a string, so objects are passed through JSON.stringify.

Example

Example

javascript

function fetch(url, options) {
  return Promise.resolve({
    ok: true,
    json: function () {
      return Promise.resolve({ sent: JSON.parse(options.body).name });
    }
  });
}

async function save() {
  const response = await fetch("/api/user", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ name: "Alan" })
  });
  const data = await response.json();
  console.log(data.sent);
}

save();

The output is Alan, read back from what was sent.

Reading Plain Text

response.text() is the right choice when the answer is not JSON.

Example

Example

javascript

function fetch(url) {
  return Promise.resolve({
    ok: true,
    text: function () { return Promise.resolve("hello"); }
  });
}

async function load() {
  const response = await fetch("/api/greeting");
  console.log(await response.text());
}

load();

Calling json() on non-JSON would throw a parsing error.

Complete Example

Complete Example

html

<!DOCTYPE html>
<html>
<head>
  <title>JavaScript Fetch API</title>
</head>
<body>

  <h1>Fetch API</h1>

  <p id="out">Loading...</p>

  <script>
    async function load() {
      const out = document.getElementById("out");

      try {
        const response = await fetch("https://jsonplaceholder.typicode.com/users/1");

        if (!response.ok) {
          throw new Error("Status " + response.status);
        }

        const user = await response.json();
        out.textContent = user.name + " from " + user.address.city;
      } catch (error) {
        out.textContent = "Failed: " + error.message;
      }
    }

    load();
  </script>

</body>
</html>

Try It Yourself

Run the above example in the Try It Editor.

Try a missing address:

Change the id to 999 and watch the status check catch it.

Important Points

  • fetch returns a promise for a response.
  • response.json() returns another promise for the data.
  • fetch does not reject on 404 or 500.
  • Check response.ok before using the body.
  • A second argument sets method, headers and body.

Conclusion

fetch is how a page talks to a server.

The two-promise shape and the response.ok trap are the two things to remember.

Everything from the earlier lessons - chaining, await, error handling - applies directly here.