Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 19

Fetching JSON Data with `fetch()`

Recommended resources

Hello! Welcome back to the course.

In our last lesson, we mastered async/await, a powerful syntax for writing clean, readable asynchronous code. We saw how it simplifies promise handling by allowing us to "pause" a function and wait for a result, all while using standard try...catch blocks for error handling.

Today, we'll put that knowledge to immediate use. We're going to learn one of the most fundamental skills for a web developer: fetching data from external APIs. This lesson focuses on using the built-in fetch() API to make GET requests and process the JSON data that comes back. This is how web applications get everything from weather updates and stock prices to user profiles and news articles.

What is an API and Why Do We Fetch?

In web development, an API (Application Programming Interface) is often a set of rules and protocols that allows one piece of software to interact with another. A REST API is a common type that allows us to request data from a server over the web using standard HTTP methods.

The fetch() function is the modern JavaScript tool for making these requests. It's built into all modern browsers, and it's based on Promises, making it a perfect fit for the async/await syntax you just learned.


Making a Basic GET Request

The simplest fetch call takes one argument: the URL of the API endpoint you want to get data from.

However, fetch has a two-step process for getting data, which can be tricky at first.

  1. Calling fetch() starts the request and returns a Promise.
  2. When that Promise resolves, it gives you a Response object, not the final data. This object contains metadata about the response, like the HTTP status and headers.
  3. To get the actual data, you need to call a method on the Response object, such as .json(), to parse the response body. This method also returns a Promise!

Let's watch a short video that introduces fetch and demonstrates this two-step process with async/await.

Fetch API - JavaScript Tutorial for beginners

This clip from the 'Fetch API' tutorial by ColorCode clearly explains the basic request/response model and shows how to get JSON data from the response.

Please watch from the beginning until 8:20. Pay close attention to how await is used twice: once for fetch() and a second time for response.json().

As you saw, the Response object gives you a lot of information.

Fetch API Successful Response in Chrome Console
This is a screenshot of a `Response` object logged in the browser's console. Note the `ok: true` and `status: 200` properties, which indicate a successful request.

The entire process is beautifully illustrated by the diagram below. The key takeaway is the two-promise chain: one for the network request itself, and a second for parsing the body of the response.

Asynchronous JavaScript Fetch Request Flow
This diagram shows the flow of a `fetch` call. The initial call returns a promise that resolves with a `Response` object. Calling `.json()` on that object returns another promise that resolves with the final parsed JavaScript object.

Putting it all together, the standard async/await pattern for fetching JSON looks like this:

async function getSomeData(url) {
  // We'll add error handling in the next section
  const response = await fetch(url);
  const data = await response.json();
  console.log(data);
}

The Most Important Part: Handling Errors Correctly

Here's a critical detail about fetch() that trips up many developers: fetch() only rejects its promise if there is a network failure.

  • If you lose your internet connection, the promise will reject, and your catch block will run.
  • However, if the server responds with an error code like 404 Not Found or 500 Internal Server Error, the fetch() promise will resolve successfully.

This means you are responsible for checking the HTTP status of the response to see if the request was actually successful. The easiest way to do this is with the response.ok property, which is true for any successful status code (in the 200-299 range).

Let's dive into the MDN documentation, the definitive source for web APIs, to formalize this concept.

Using the Fetch API - MDN Web Docs

The article 'Using the Fetch API' from MDN Web Docs is an essential read. We'll focus on the sections that explain how to handle the response and check its status.

Read the section titled 'Handling the response'. Pay special attention to the subsections 'Checking response status' and 'Reading the response body'. This will solidify the pattern for robust error checking.

Based on this, our robust pattern for fetching data becomes:

async function getSomeData(url) {
  try {
    const response = await fetch(url);

    // Check if the response was successful
    if (!response.ok) {
      // If not, throw an error with the status
      throw new Error(`HTTP error! Status: ${response.status}`);
    }

    const data = await response.json();
    console.log(data);

  } catch (error) {
    // This will catch network errors and the error we threw above
    console.error("Could not fetch data:", error);
  }
}

// Example of a bad request that our code will handle
getSomeData('https://pokeapi.co/api/v2/pokemon/this-does-not-exist');
Test your understanding!

Consider the robust getSomeData function above. What would be logged to the console in the following two scenarios?

  1. Your computer is disconnected from the internet when you call getSomeData('https://pokeapi.co/api/v2/pokemon/ditto').
  2. Your computer is connected to the internet, but you call getSomeData('https://pokeapi.co/api/v2/pokemon/spongebob').
Show answer
  1. Scenario 1 (No Internet): The fetch() call itself will fail due to a network error. The promise will reject, and the catch block will execute. The console will log something like: Could not fetch data: TypeError: Failed to fetch.

  2. Scenario 2 (Bad URL): The fetch() call will succeed because it successfully connects to the server. However, the server will respond with a 404 Not Found status. Our if (!response.ok) check will be true, causing us to throw new Error(...). This error is then caught by the catch block. The console will log: Could not fetch data: Error: HTTP error! Status: 404.


Practical Application: Fetching Pokémon Data

Now let's apply this to a small, practical project. Since your goal is to build web applications, seeing how fetch interacts with the user interface is a great next step.

The following video walks through building a tiny app that fetches Pokémon data based on user input and displays the Pokémon's image (its "sprite"). This will give you a preview of DOM manipulation, which we'll cover in the next module.

How to FETCH data from an API using JavaScript ↩️

This video from Bro Code, 'How to FETCH data from an API using JavaScript', demonstrates everything we've learned in a fun, interactive example.

Please watch from 6:20 to the end. The first part (from 6:20) shows the async/await implementation with error handling, which will be a great review. The second part (from 8:54) builds the interactive HTML page. Don't worry about memorizing the DOM manipulation code; focus on how fetch is used to get and display dynamic data.

This project is a perfect example of the "80/20" principle you value: a simple fetch call is the 20% of effort that provides 80% of the dynamic functionality in many web apps.


Conclusion

You've just learned a cornerstone of modern web development. By combining the power of async/await with the fetch() API, you can now retrieve data from countless services across the web and start building truly dynamic applications.

Key Takeaways:

  • The fetch() API is the standard for making HTTP requests in JavaScript.
  • Fetching data is a two-promise process: first fetch(url) resolves to a Response object, then response.json() resolves to the actual data.
  • Error handling is crucial: fetch() only fails on network errors. You must always check response.ok to handle HTTP errors like 404s or 500s.
  • The standard pattern combines async/await with a try...catch block and an if (!response.ok) check for robust data fetching.

In our next lesson, we'll cover the other side of the coin: sending data to an API using POST requests. This will enable us to create new resources on a server, a key step toward building full-stack applications.