Fetch

The Fetch APIopen in new window is a replacement for the older XMLHttpRequestopen in new window and can be used to make Ajax requests. With a fetch request, the Fetch API will return a response in the form of a JavaScript Promiseopen in new window.

Basic Fetch Request

In many cases, the basic fetch request, relying on the default setting, can be sufficient. The following example is a basic fetch request that returns JSON data.

fetch('https://jsonplaceholder.typicode.com/users/1')
      .then(response => response.json())
      .then(json => document.getElementById("output").innerHTML = JSON.stringify(json))

Displaying JSON with JSON.stringify()

JSON (JavaScript Object Notation) is a text format commonly used to exchange data with an API. After we read a JSON response as a JavaScript object, JSON.stringify() can convert that object into a JSON string so we can display its contents as text.

const user = { name: 'Alex', email: 'alex@example.com' }

console.log(JSON.stringify(user))
// {"name":"Alex","email":"alex@example.com"}

In these examples, response.json() reads and parses the response body into a JavaScript value, while JSON.stringify() converts that value back into text for display. To make the output easier to read, JSON.stringify(user, null, 2) keeps all properties and adds line breaks and two-space indentation. Display this formatted text in a <pre> element to preserve the spacing.

Using Async and Await

An asyncopen in new window function, in conjunction with the await keyword, enables asynchronous, promise-based behavior to be written in a cleaner style, avoiding the need for chaining. Async functions can provide an alternative syntax for working with the fetch.

Add an output element to your HTML:

<pre id="output"></pre>
async function fetchData() {
  const response = await fetch(
    'https://jsonplaceholder.typicode.com/users/1'
  )
  const json = await response.json()

  document.querySelector('#output').textContent =
    JSON.stringify(json, null, 2)
}

fetchData()

Defining fetchData() does not run it; the final line calls it. The first await waits for the response. The second reads and parses the response body as JSON.

Handling Fetch Errors

A request might fail because the browser is offline, the server cannot be reached, or the requested resource does not exist. The response body might also contain data that cannot be parsed as JSON. Handling these situations lets us show a useful message instead of leaving the user without feedback.

HTTP errors and network failures

An HTTP error response is different from a network failure:

  • HTTP error: The server responds, but reports a problem, such as 404 (not found) or 500 (server error). fetch() still resolves with a response, so we must check its status.
  • Network failure: The browser cannot complete the request. The promise returned by fetch() rejects. A rejected promise at an await inside try transfers control to catch.

The response.ok property is true for HTTP status codes from 200 to 299. Checking !response.ok means checking whether the response was unsuccessful.

Add error handling to the function

Use the same <pre id="output"></pre> element from the previous example. Replace the previous JavaScript example with this version rather than adding a second fetchData() definition and call. Ensure the output element exists before calling the function, for example by loading an external script with defer.

async function fetchData() {
  const output = document.querySelector('#output')
  output.textContent = 'Loading data...'

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

    if (!response.ok) {
      throw new Error('HTTP error: ' + response.status)
    }

    const json = await response.json()
    output.textContent = JSON.stringify(json, null, 2)
  } catch (error) {
    output.textContent = 'Unable to load the data. Please try again.'
    console.error(error)
  }
}

fetchData()

Show loading feedback

Before the request begins, output.textContent = 'Loading data...' replaces any previous result or error message. This tells the user that a new request is in progress.

When the request succeeds, the formatted JSON replaces the loading message. If the request fails, the catch block replaces it with the error message. Calling fetchData() again starts this sequence over.

How the error handling works

  1. try contains the work that might fail: making the request, checking the response, and reading the JSON.
  2. if (!response.ok) checks for an unsuccessful HTTP response before trying to use its body.
  3. throw new Error(...) creates and throws an error containing the HTTP status. Execution stops in the try block and moves to catch; the JSON-reading step is skipped.
  4. await response.json() reads and parses the response body. If it is not valid JSON, parsing rejects and control also moves to catch.
  5. catch (error) receives the error. It shows a short message on the page and logs technical details to the console for debugging.

When every step succeeds, the JSON is displayed and the catch block is skipped. JSON.stringify(json, null, 2) formats the data with two-space indentation, and textContent displays it as text rather than interpreting it as HTML.

The error message does not retry the request automatically. After resolving the problem, call fetchData() again from the console or refresh the page.

Try it

  • Run the example with the original URL and confirm that the data appears.
  • Change the endpoint to https://jsonplaceholder.typicode.com/users/999999 and run it again. Inspect the Network panel to confirm the status returned by the service, then check the page message and console error.
  • Restore the original URL. With the page already loaded, select Offline in the browser's Network panel and call fetchData() from the console. Check that the failure is handled, then turn Offline mode off and call the function again.

For more detail, see Using the Fetch APIopen in new window and try...catchopen in new window.

Displaying Data from an API

Displaying the full JSON response is useful while exploring an API. For a webpage, we usually choose specific properties from the returned object and display them in HTML elements. This example displays a user's name and email address.

Replace the previous output element and JavaScript with the following examples. Load your external JavaScript file with defer so the HTML elements are available when the function runs.

<p id="status" role="status"></p>
<h2 id="user-name"></h2>
<p id="user-email"></p>
async function fetchUser() {
  const status = document.querySelector('#status')
  const userName = document.querySelector('#user-name')
  const userEmail = document.querySelector('#user-email')

  status.textContent = 'Loading user...'
  userName.textContent = ''
  userEmail.textContent = ''

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

    if (!response.ok) {
      throw new Error('HTTP error: ' + response.status)
    }

    const user = await response.json()
    userName.textContent = user.name
    userEmail.textContent = 'Email: ' + user.email
    status.textContent = 'User loaded.'
  } catch (error) {
    status.textContent = 'Unable to load the user. Please try again.'
    console.error(error)
  }
}

fetchUser()

From an object to page content

  1. Select the elements. The three variables hold references to the HTML elements we will update.
  2. Prepare the page. Show a loading message and clear the previous user's details. This prevents old details from remaining visible if another request fails.
  3. Read the object. await response.json() parses the response body. This endpoint returns one user object, which we store in user.
  4. Access its properties. user.name and user.email use dot notation to read values from that object, just like the objects you created earlier in the course.
  5. Update the DOM. Assigning each value to textContent displays it as text. We no longer need JSON.stringify() because we are displaying individual string properties instead of the whole object.

The loading, success, and error messages use a separate status element. Its role="status" helps assistive technology announce changes without moving keyboard focus. The request still checks response.ok and uses try...catch, as in the previous example.

Try it

  • Change /users/1 to /users/2 and confirm that the displayed name and email change.
  • Add another paragraph to the HTML and display user.phone. Select the new element, clear it when the request starts, and update it after the JSON is parsed.
  • After a successful request, turn on Offline in the browser's Network panel and call fetchUser() again. Confirm that the old details are cleared and the error message appears. Turn Offline mode off when finished.

This YouTube video was created by Steve Griffith.