Consuming APIs with Fetch

Discover how to fetch data from servers and bring dynamic content to your websites using JavaScript's powerful Fetch API.

Lesson ProgressStep 1 of 9
📡➡️ 🗄️🤝🛡️🚀
0 EXP

Hello! Let's learn how to get data from a server using the JavaScript Fetch API.

// Starting our data journey... 📡

What is an API?

An **API (Application Programming Interface)** is a set of rules that allows one software application to talk to another. In web development, we typically use web APIs to request data from a server.

Think of it like a restaurant menu: the menu (API) lists the dishes you can order (requests). You give your order to the waiter (`fetch`), who brings it to the kitchen (server). The kitchen prepares your dish (data) and the waiter brings it back to you.

This data is usually formatted as **JSON (JavaScript Object Notation)**, which is easy for JavaScript to understand.

System Check

What does API stand for?

Advanced Holo-Simulations

0 EXP

Log in to unlock these advanced training modules and test your skills.


Achievements

🏆
Data Retriever

Make a successful GET request using `fetch()`.

⛓️
Promise Pro

Correctly chain `.then()` and `.catch()`.

📦
Special Delivery

Send data using a POST request.

Mission: Retrieve Live Data

Fetch data from `https://jsonplaceholder.typicode.com/todos/1` and log the resulting data. Our AI assistant will provide real-time feedback.

A.D.A. Feedback:

> Awaiting input...

Challenge: Order the `async/await` flow

Drag the code blocks into the correct order to build a valid `async/await` function.

const res = await fetch(URL);
async function getData() {
console.log(data);
const data = await res.json();
}

Challenge: Complete the POST Request

Fill in the missing parts to send a JSON object as a POST request.

fetch(URL, {
: 'POST',
headers: {
'Content-Type':
},
body: JSON.(data)
});

Consult A.D.A.

Community Holo-Net

Fetch API: The Modern Way to Request Data

In the world of modern web development, applications are rarely static. They are alive with data fetched from servers, updated in real-time, and sent back to be stored. The backbone of this communication is the Fetch API, a powerful, Promise-based interface built directly into the browser (and available in environments like Node.js) for making network requests.

It's the modern successor to the older `XMLHttpRequest` (XHR), offering a cleaner, more flexible, and more powerful API.

Part 1: The Basic GET Request & Promise Chain

The simplest `fetch()` call takes one argument: the URL of the resource you want to retrieve. It immediately returns a **Promise**. A Promise is a placeholder object for a future value—in this case, the server's response.

We handle this Promise by chaining `.then()` methods:

  • The first `.then()`: Receives a `Response` object. This object doesn't contain your data directly; it's a wrapper for the entire HTTP response. You must use a method like `.json()` to parse the response body.
  • The `.json()` method: This also returns a Promise, which resolves with the actual data, parsed as a JavaScript object.
  • The second `.then()`: Receives the fully parsed data, ready for you to use.
  • The `.catch()` block: This is crucial. It only triggers on a **network failure** (e.g., user is offline, server can't be reached).
const API_URL = 'https://jsonplaceholder.typicode.com/todos/1';

fetch(API_URL)
  .then(response => {
    // We'll add an error check here later
    return response.json();
  })
  .then(data => {
    console.log(data); // { userId: 1, id: 1, title: '...', completed: false }
  })
  .catch(error => {
    console.error('Fetch Error:', error); // Only catches network failures
  });

The `fetch()` Error Handling Quirk

This is the single most important concept to understand about `fetch()`: **It does NOT reject on HTTP error statuses like 404 or 500.**

A `fetch()` Promise only rejects if there's a network-level failure. If you request a page that doesn't exist (a 404), as far as `fetch()` is concerned, the request was "successful" because the server *did* send a response (the 404 page).

To handle this, you must manually check the `response.ok` property:

❌ Bad Practice

fetch('api/does-not-exist')
  .then(res => res.json())
  .then(data => ...)
  .catch(err => {
    // This will NOT run on a 404!
  });

✔️ Good Practice

fetch('api/does-not-exist')
  .then(response => {
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }
    return response.json();
  })
  .then(data => ...)
  .catch(err => {
    // This WILL now catch the error!
  });

Part 2: The `async/await` Syntax

Promise chains are powerful, but they can be hard to read. Modern JavaScript (ES2017+) introduced `async/await` syntax, which is just "syntactic sugar" on top of Promises. It lets you write asynchronous code that *looks* synchronous.

  • `async` function: You declare a function with the `async` keyword. This automatically makes it return a Promise.
  • `await` keyword: Inside an `async` function, you can use `await` to "pause" the function's execution until a Promise settles.
  • Error Handling: You use a standard `try...catch` block, which is much more intuitive.
const getTodo = async () => {
  try {
    const response = await fetch('https://jsonplaceholder.typicode.com/todos/1');

    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }

    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error('Error fetching data:', error);
  }
};

getTodo();

Part 3: Sending Data (POST, PUT, DELETE)

`fetch()` isn't just for getting data. To send data, you pass a second argument: an **options object**.

const newTodo = {
  userId: 10,
  title: 'Learn Fetch API',
  completed: false
};

fetch('https://jsonplaceholder.typicode.com/posts', {
  method: 'POST', // Specify the method
  headers: {
    'Content-Type': 'application/json' // Tell the server we're sending JSON
  },
  body: JSON.stringify(newTodo) // Convert the JS object to a JSON string
})
.then(response => response.json())
.then(data => {
  console.log('Success:', data); // Logs the new post (with id) from the server
})
.catch(error => {
  console.error('Error:', error);
});

Part 4: Aborting a Request

What if a user clicks away before a large request finishes? You can abort a `fetch()` request using an `AbortController`.

// Create a controller
const controller = new AbortController();
const signal = controller.signal;

// Pass the signal to fetch
fetch('https/api.example.com/very-large-file', { signal })
  .then(...)
  .catch(err => {
    if (err.name === 'AbortError') {
      console.log('Fetch aborted');
    }
  });

// To abort:
controller.abort();
Key Takeaway: The Fetch API is the modern standard for network requests. Master the `async/await` syntax, and **always remember to manually check for `response.ok`** to handle HTTP errors correctly.

Fetch API & Asynchronicity Glossary

API
**Application Programming Interface**. A set of rules allowing different software applications to communicate with each other. A web API allows you to get data from a server.
Asynchronous
Code that doesn't run in sequence. Asynchronous operations (like `fetch()`) run in the background, allowing the rest of your code to continue executing without waiting.
`fetch()`
The browser-native function for making network requests. It is Promise-based and replaces the older `XMLHttpRequest`.
Promise
An object representing the eventual completion (or failure) of an asynchronous operation. It can be in one of three states: **pending**, **fulfilled**, or **rejected**.
`.then()`
A method called on a Promise to handle its fulfilled (successful) state. It receives the value the Promise resolved with.
`.catch()`
A method called on a Promise to handle its rejected (failed) state. It receives the error that was thrown.
`Response`
The object returned by the first `.then()` in a `fetch()` chain. It contains the response status, headers, and body (which needs to be parsed).
`response.ok`
A boolean property on the `Response` object. It is `true` if the HTTP status code is in the 200-299 range (i.e., successful).
`.json()`
A method on the `Response` object that reads the response body to completion and parses it as JSON. It returns a Promise.
`async`
A keyword that declares a function as asynchronous. It ensures the function always returns a Promise.
`await`
A keyword that can only be used inside an `async` function. It pauses the function's execution until the Promise it's "awaiting" settles, and then returns the resolved value.
`Headers`
An object containing the HTTP headers of a request or response, such as `Content-Type`, `Authorization`, etc.
`body`
The property in the `fetch()` options object used to send data (e.g., a JSON string) to the server.
CORS
**Cross-Origin Resource Sharing**. A browser security mechanism that restricts HTTP requests from one domain to another unless the server explicitly allows it.
`AbortController`
A built-in interface used to signal and abort asynchronous operations, most commonly `fetch()` requests.

About the Author

Author's Avatar

TodoTutorial Team

Passionate developers and educators making programming accessible to everyone.

This article was written and reviewed by our team of web development experts, who have years of experience teaching JavaScript and building robust, data-driven web applications.

Verification and Updates

Last reviewed: October 2025.

We strive to keep our content accurate and up-to-date. This tutorial is based on the latest ECMA2025 specifications and is periodically reviewed to reflect industry best practices.

External Resources

Found an error or have a suggestion? Contact us!