JavaScript Fetch API

javascript-fetch-api.jpg

In the beginning, the internet was just a static library of pages. But all that has changed in the past few decades, and it’s largely thanks to JavaScript. Search for tomorrow’s weather, and the answer appears without the page even having to reload. Even filling out a form feels effortless now, no clunky redirects, no waiting game.

At the center of it all is the JavaScript Fetch API. Think of it as a clean, modern way for your app to talk to a server. Old methods like XMLHttpRequest got the job done, but they were clumsy and hard to manage. Fetch in JavaScript, on the other hand, is promise-based, easy to read, and works beautifully with async/await.

In this guide, we’ll walk through how the fetch() function in JavaScript works, why it’s better than older approaches, and how you can use it to make your own applications faster and more user-friendly.

Table of Contents:

What is Fetch in JavaScript?

The JavaScript Fetch API is the modern and more flexible way to make HTTP requests directly from the browser. It is the tool that facilitates the communication between the browser and the server to “fetch” data of various kinds, including headlines for a news feed and saving form details.

What separates the JavaScript Fetch API from the rest is its design. Instead of being reliant on the old XMLHttpRequest, Fetch uses JavaScript Promises to get the job done. This makes your code extremely readable, and your application runs smoothly even when it is waiting for data from a server.

In short, Fetch in JavaScript helps your application stay fast, interactive, and easy to maintain, no unnecessary complexity, no freezing screens.

Why Use Fetch in JavaScript Instead of XMLHttpRequest?

Before the JavaScript Fetch API was introduced in ES6 (ECMAScript 2015), developers were using XMLHttpRequest. It worked just fine, but it often felt like forcing a square peg into a round hole. From long syntax and nested callbacks to error handling that could get messy fast, it was a nightmare to work with. Fetch was designed to fix all of that. Here’s why most developers now prefer it:

  • Cleaner syntax: Code is shorter and easier to write.
  • Promise-based: Works seamlessly with async/await, so your logic reads like normal sentences instead of tangled callbacks.
  • More powerful: Supports all the common HTTP methods (GET, POST, PUT, DELETE), custom headers, credentials, and different response types like JSON, text, and files.
  • Better browser support: It’s built into all modern browsers and works smoothly with frameworks like React, Vue, and Angular.

Key Features of the JavaScript Fetch API

  1. Asynchronous requests using Promises
  2. Easy-to-read syntax for HTTP methods
  3. Built-in support for JSON and other response formats
  4. Handles cross-origin requests (CORS) gracefully
  5. Ability to cancel requests using AbortController
  6. Efficient streaming of large responses

How the Fetch API Works

The Fetch API provides a modern way to make network requests in JavaScript. Here’s the typical flow:

  1. Send a request: You call fetch(url) to initiate an HTTP request.
  2. Server processes the request: The server receives it, performs the required logic (like fetching data from a database), and sends back a response.
  3. Handle the response: The response is returned as a Response object. You usually convert it into a usable format, such as JSON using .json(), or text with .text().
  4. Handle errors: Any failures, like network errors or invalid responses, can be caught with .catch() when using promises, or inside a try…catch block when using async/await.

JavaScript Fetch API Syntax and Example

Now that you have an understanding of how the Fetch API in JavaScript works under the hood, let’s have a look at its syntax.

JavaScript Fetch API Syntax

The simplest form of a JavaScript Fetch API looks like this:

fetch(url, options)
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));

The fetch() function in JavaScript can accept two parameters: 

  1. url (required): The URL of the resource you want to fetch.
  2. options (optional):
    1. method: ‘GET’, ‘POST’, ‘PUT’, ‘DELETE’, etc.
    2. headers: Custom HTTP headers.
    3. body: Data to send with the request (for POST/PUT).
    4. credentials: Include cookies or authentication information.


In the example above:

.then(response => response.json()): gets the HTTP response and parses the body into JSON (returns a Promise).

.then(data => console.log(data)): receives the parsed JSON and logs it.

.catch(error => console.error(error)): handles any error (network issues, bad JSON, etc.).

Using async/await with Fetch API

You can also use async/await for cleaner syntax, like so:

async function fetchData(url) {
  try {
    const response = await fetch(url);
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error('Error:', error);
  }
}
fetchData('https://api.example.com/data');

GET Request with JavaScript Fetch API

The JavaScript Fetch API is designed in such a way that by default, it uses the GET method. 

fetch("https://api.example.com/data", {
  method: "GET",
  headers: {
    "Authorization": "Bearer token123"
  }
})
  .then(response => response.json())
  .then(data => console.log("Success:", data))
  .catch(error => console.error("Error:", error));

Adding Headers

Sometimes you need to include headers, such as authentication tokens or content preferences:

fetch("https://api.example.com/data", {
  method: "GET",
  headers: {
    "Authorization": "Bearer token123"
  }
})
  .then(response => response.json())
  .then(data => console.log("Success:", data))
  .catch(error => console.error("Error:", error));
  • Authorization: Used for API keys or tokens.
  • Accept: Tells the server the response format you expect (e.g., application/json).

Sending Query Parameters

GET requests don’t have a body. Instead, you pass extra data in the URL using query parameters:

const params = new URLSearchParams({ search: "JavaScript", page: 1 });
fetch(`https://api.example.com/search?${params}`)
  .then(response => response.json())
  .then(data => console.log(data));

Useful for filtering, sorting, or paginating results.

The Response Object

The fetch() function in JavaScript returns a Promise that resolves to a Response object. This object contains:

  • Status information (status, statusText)
  • Response headers (headers)
  • Methods to read the body (json(), text(), blob(), etc.)

POST/PUT/DELETE Request with JavaScript Fetch API

Now that you know the basics of how to make a GET request using fetch() in JavaScript, the next step is to learn how to make a POST request using the fetch API (send data to the server). This is how you submit a form, update a record, or post JSON. With Fetch, you have full control over the process.

  1. Setting the Fetch Method in JavaScript

If you want to send data or perform another action, you will need to set the HTTP method manually in the options object.

fetch('https://api.example.com/users', {
  method: 'POST' // You can also use PUT, DELETE, PATCH, etc.
});

This tells the server what you want to do, whether it’s adding new data, updating something, or deleting a record.

  1. Adding a Request Body

Now we know how to tell the server what to do with our data. Now, for the data itself, we will have to add a body and place our data (usually JSON) in it.

fetch('https://api.example.com/users', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    name: 'Intellipaat',
    email: '[email protected]'
  })
})
  .then(response => response.json())
  .then(data => console.log(data));

Here, the headers tell the server the format of the data, and body carries the actual information.

  1. Handling Custom Headers

Headers can also add extra information to your request; some of the most commonly used ones are:

  • Content-Type: tells the server the format of your request (like application/json).
  • Authorization:  used for API keys, tokens, or authentication.

Example with custom headers:

fetch('https://api.example.com/data', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer your-api-token'
  },
  body: JSON.stringify({ key: 'value' })
});
  1. Sending Data in a GET Request

Normally, GET requests don’t have a body. Instead, you pass data as query parameters in the URL:

const params = new URLSearchParams({ search: 'JavaScript', page: 1 });
fetch(`https://api.example.com/search?${params}`)
  .then(response => response.json())
  .then(data => console.log(data));

This is especially handy when fetching filtered, sorted, or paginated data.

Handling Requests, CORS, and Canceling in JavaScript Fetch

If you are even a little bit familiar with APIs, you will know that in the real world, making a request is hardly as straightforward as the GET or POST examples we have seen so far. Most of the time, you will need to deal with cross-origin rules, send credentials, customize requests, or even stop halfway. Let’s discuss how fetch handles those situations.

  1. Cross-Origin Requests (CORS)

For security, browsers enforce CORS (Cross-Origin Resource Sharing). If your site tries to fetch data from another domain, that server has to explicitly allow it.

fetch('https://api.example.com/data', {
  mode: 'cors'
})
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('CORS error:', error));

Here, mode: ‘cors’ tells the browser to attempt a cross-origin request. If the server doesn’t send back the right headers, the request will fail.

  1. Including Credentials

Some APIs require extra information like cookies or authentication. In those cases, you can add the credentials option:

fetch('https://api.example.com/data', {
  credentials: 'include' // or 'same-origin'
});
  • include: sends cookies even for cross-origin requests.
  • same-origin: sends cookies only if the request comes from the same domain.
  1. Creating a Request Object

Instead of passing the URL and options directly, you can create a Request object. This gives you more flexibility, especially if you want to reuse or tweak the request later:

const request = new Request('https://api.example.com/data', {
  method: 'GET',
  headers: { 'Accept': 'application/json' }
});
fetch(request)
  .then(response => response.json())
  .then(data => console.log(data));

This approach is cleaner when working with multiple similar requests.

  1. Canceling a Request with AbortController

Sometimes the request becomes unnecessary, maybe the user navigated away, or it’s simply taking too long. With AbortController, you can stop it in its tracks:

const controller = new AbortController();
const signal = controller.signal;
fetch('https://api.example.com/data', { signal })
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => {
    if (error.name === 'AbortError') {
      console.log('Fetch aborted');
    } else {
      console.error('Fetch error:', error);
    }
  });
// Cancel the request after 2 seconds
setTimeout(() => controller.abort(), 2000);

By canceling unnecessary requests, you save resources and keep your app feeling snappy. 

Handling the Response in JavaScript Fetch

After sending a request with the JavaScript Fetch API, the next step is handling the response. Fetch makes it easy to check status codes, read headers, and process different types of response bodies, including JSON, text, blobs, and streams.

  1. Checking Response Status

Every Fetch request returns a Response object. You can check the HTTP status code to ensure the request succeeded:

fetch('https://api.example.com/data')
  .then(response => {
    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }
    return response.json();
  })
  .then(data => console.log(data))
  .catch(error => console.error('Fetch error:', error));
  • response.ok is true for status codes 200–299.
  • response.status gives the exact HTTP code.
  1. Checking the Response Type

The JavaScript Fetch API supports multiple response types:

fetch('https://api.example.com/data')
  .then(response => {
    console.log('Content-Type:', response.headers.get('Content-Type'));
    return response.json(); // or .text(), .blob(), .arrayBuffer()
  })
  .then(data => console.log(data));
  • Use .json() for JSON data.
  • Use .text() for plain text.
  • Use .blob() for images, videos, or files.
  1. Reading Headers

Headers provide additional metadata about the response:

fetch('https://api.example.com/data')
  .then(response => {
    console.log('All Headers:', [...response.headers]);
    console.log('Server:', response.headers.get('Server'));
  });

You can access individual headers or loop through all headers using the Headers API.

  1. Reading the Response Body

The response body can be consumed using Promises:

fetch('https://api.example.com/data')
  .then(response => response.json())
  .then(data => console.log(data));

Once a response is read, it cannot be read again unless you clone it:

fetch('https://api.example.com/data')
  .then(response => {
    const clone = response.clone();
    return response.json();
  });
  1. Streaming the Response Body

Fetch also supports streaming large responses, allowing you to process data incrementally:

fetch('https://api.example.com/large-file')
  .then(response => {
    const reader = response.body.getReader();
    return reader.read().then(function process({ done, value }) {
      if (done) return;
      console.log('Chunk received:', value);
      return reader.read().then(process);
    });
  });
  1. Processing a Text File Line by Line

You can handle line-by-line processing with streaming:

fetch('https://api.example.com/large-text')
  .then(response => response.body.getReader())
  .then(reader => {
    const decoder = new TextDecoder();
    function readChunk() {
      reader.read().then(({ done, value }) => {
        if (done) return;
        console.log(decoder.decode(value));
        readChunk();
      });
    }
    readChunk();
  });

Error Handling and Debugging in JavaScript Fetch

Like a lot of things in programming, making server requests can often go wrong. Sometimes the server is down, the network drops, or the response isn’t what you expected. If you want your code to be reliable, you will need to learn how to handle these errors.

  1. Network Errors

If the browser is unable to reach the server, Fetch will throw an error. You can use .catch() or a try…catch to handle the same, like so:

fetch('https://api.invalid-url.com/data')
  .then(response => response.json())
  .catch(error => console.error('Network error:', error));
  1. Handling Bad Status Codes

It gets a little tricky here because Fetch in JavaScript does not throw an error for 404 or 500 responses. It only rejects if there is a network connection issue and to handle failed status codes, you will have to check response.ok yourself.

fetch('https://api.example.com/data')
  .then(response => {
    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }
    return response.json();
  })
  .then(data => console.log(data))
  .catch(error => console.error('Fetch error:', error));

This way, you can catch errors like “Not Found” or “Server Error” instead of silently failing.

  1. Using try…catch with Async/Await

With async/await, error handling feels more natural:

async function getData() {
  try {
    const response = await fetch('https://api.example.com/data');
    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error('Error:', error);
  }
}
  1. Common JavaScript Fetch Pitfalls
  • Forgetting to check response.ok:  you miss 404/500 errors.
  • Using the wrong Content-Type header: the server may reject your request.
  • Reading the response twice: once you consume a response body, you can’t read it again unless you clone it.
  1. Fetch in JavaScript Debugging Tips
  • Log the status code and headers to see what the server is sending back.
  • Use tools like Chrome DevTools or Postman to test requests separately.
  • Start small, test the API with a simple fetch() before adding options like headers or body.

Good error handling doesn’t just stop your app from breaking; it also makes debugging faster and your user experience smoother.

Fetch API JavaScript Examples

At this point, Fetch in JavaScript should feel less like an abstract tool and more like a versatile instrument in your kit. But the real fun begins when you see it in action. Let’s step out of the documentation bubble and look at how Fetch powers everyday apps.

  1. Fetching Data from an API (Basic Example)

Let’s say you want to pull a list of posts from a public API:

fetch('https://jsonplaceholder.typicode.com/posts')
  .then(response => response.json())
  .then(posts => {
    console.log('Posts:', posts);
  })
  .catch(error => console.error('Error fetching posts:', error));

This is the bread-and-butter use case for Fetch: grabbing data from a server and working with it in your app.

  1. Weather App Example

Now let’s add a dash of creativity. Suppose you’re building a lightweight weather widget. You could use any free weather API and display current conditions for your city:

const city = 'London';
const apiKey = 'your_api_key_here';
fetch(`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}&units=metric`)
  .then(response => response.json())
  .then(weather => {
    console.log(`${weather.name}: ${weather.main.temp}°C, ${weather.weather[0].description}`);
  })
  .catch(error => console.error('Error fetching weather:', error));

In just a few lines, you can connect your app to a global weather service. The output might read something like:

London: 22°C, scattered clouds

That’s Fetch in the wild, powering real, user-facing experiences.

  1. Submitting a Form with Fetch in JavaScript

Fetching isn’t just about grabbing data; it also shines when you need to send information back to a server. Picture a signup form on your website. Instead of the old page refresh method, you can send data smoothly with the JavaScript Fetch API:

const formData = {
  name: 'Intellipaat',
  email: '[email protected]'
};
fetch('https://jsonplaceholder.typicode.com/posts', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(formData)
})
  .then(response => response.json())
  .then(result => {
    console.log('Form submitted successfully:', result);
  })
  .catch(error => console.error('Error submitting form:', error));

This makes the experience seamless: no reloads, no jarring flickers, just smooth, modern interaction powered by Fetch.

Conclusion

The JavaScript Fetch API is more than just a modern replacement for XMLHttpRequest; it’s your gateway to building smooth, responsive web applications. From fetching data to submitting forms, handling errors, and even canceling requests, Fetch gives you the tools to interact with APIs effortlessly.

If you enjoyed exploring Fetch and want to take your skills further, our Full Stack Web Development course guides you from JavaScript basics to building complete, real-world applications. With hands-on projects, real APIs, and industry-relevant exercises, it’s designed to turn concepts into experience and ideas into live websites.

Fetch API – Frequently Asked Questions

1. Is Fetch API better than XMLHttpRequest (XHR)?

Yes. Fetch API is more modern, cleaner, and Promise-based, while XMLHttpRequest uses callbacks, which can get messy. Fetch also supports streaming and better error handling. Unless you’re supporting very old browsers, Fetch is preferred.

2. Does Fetch API work in all browsers?

Fetch API is supported in most modern browsers (Chrome, Edge, Firefox, Safari). However, Internet Explorer does not support it, so you’d need a polyfill if you must support IE.

3. Can Fetch API send cookies and authentication data?

Yes. By default, Fetch does not include cookies. To send them, use the credentials option:

fetch("https://example.com/api", {
  credentials: "include"
})

This tells Fetch to include cookies and session data in the request.

4. How do I cancel a Fetch request in JavaScript?

You can use the AbortController API. Example:

const controller = new AbortController();
fetch("https://api.example.com/data", { signal: controller.signal });
controller.abort(); // cancels the request

This is useful for timeouts or when a user navigates away.