Skip to Content
Course content

64: Fetching Data with the Fetch API

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I want to show you how we actually get data into our apps from the outside world. For a long time, we had to deal with XMLHttpRequest, which was—to put it politely—a nightmare to write. Now we have the Fetch API. It's cleaner, but there's a specific rhythm to it that trips everyone up the first time.

Let's try to grab a fake blog post from JSONPlaceholder, a free API used for testing. I'll start with the simplest possible call.

Wait, where's my data?

const data = fetch('https://jsonplaceholder.typicode.com/posts/1');
console.log(data);

If you run that, you're probably expecting to see a post object in the console. Instead, you'll see something like Promise {<pending>}. This is the classic "first-time fetch" mistake. I did it for weeks when I started.

The fetch function doesn't return the data immediately because network requests take time. It returns a Promise—essentially a placeholder that says, "I'll let you know when the server actually responds." To get the actual value, we need to wait for that promise to resolve.

fetch('https://jsonplaceholder.typicode.com/posts/1')
  .then(response => {
    console.log(response);
  });

Unwrapping the Response

Okay, we've moved past the pending promise. But look at the console now. We didn't get our blog post; we got a Response object. It has a status code (hopefully 200) and some headers, but the actual body of the message is still essentially a raw stream of bytes.

We need to tell JavaScript how to interpret that stream. Since we know this API sends JSON, we use the .json() method. Here is the kicker: .json() also returns a promise. So we have to chain another .then().

fetch('https://jsonplaceholder.typicode.com/posts/1')
  .then(response => response.json()) 
  .then(post => {
    console.log('Here is the actual post:', post.title);
  });

It works, but this "callback hell" style of chaining gets ugly fast when you have to do multiple things in a row. This is why I almost always use async and await. It makes asynchronous code look and behave like synchronous code.

Cleaning up the syntax

Let's rewrite that. To use await, we have to wrap the logic in a function marked as async. I like this approach because it reads like a story: "Fetch this, then wait for the JSON, then log it."

async function getPost() {
  const response = await fetch('https://jsonplaceholder.typicode.com/posts/1');
  const post = await response.json();
  console.log('Much cleaner:', post.title);
}

getPost();

This is significantly easier to reason about. However, we're ignoring a huge reality of software engineering: the internet is flaky. Servers crash, DNS fails, and users lose their Wi-Fi. If the fetch call fails right now, the whole script will throw an uncaught error and crash.

When things go south

We need a safety net. In async/await land, that means a try...catch block. But there's a nuance here: fetch only "fails" (triggers the catch block) if there is a network error. If the server responds with a 404 (Not Found) or a 500 (Internal Server Error), fetch actually considers that a "success" because it technically received a response.

To handle this properly, we check the ok property of the response object first.

async function getPostSafely() {
  try {
    const response = await fetch('https://jsonplaceholder.typicode.com/posts/9999'); // ID that doesn't exist
    
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    const post = await response.json();
    console.log(post);
  } catch (error) {
    console.error('Something went wrong:', error.message);
  }
}

getPostSafely();

Now we're writing production-ready code. We've handled the promise, parsed the JSON, cleaned up the syntax, and accounted for the inevitable failure of the network. You're ready to start pulling real data into your UI.




📋 Practical Task

The Random Identity Generator

Your task is to build a small script that fetches a random user's profile from the randomuser.me API and displays it in the console.

Requirements:

  • Create an async function called generateUser.
  • Fetch data from: https://randomuser.me/api/
  • Implement a try...catch block to handle any potential network errors.
  • Check if response.ok is true before parsing the JSON.
  • The API returns an object where the user data is inside an array called results. You need to access the first element: data.results[0].
  • Log a string to the console in this format: "User Found: [FirstName] [LastName] from [City]".
Rating
0 0

There are no comments for now.

to be the first to leave a comment.