The Complete Guide to JavaScript‘s Fetch API
If you‘ve done web development with JavaScript, you‘re probably familiar with making HTTP requests to interact with APIs, load data from your server, submit forms, and more. For many years, XMLHttpRequest was the main way to send requests in JavaScript. But in recent years, a newer, more modern and powerful API has emerged – the Fetch API.
In this in-depth guide, we‘ll teach you everything you need to know about the JavaScript Fetch API. You‘ll learn what it is, how to use it, best practices, and advanced techniques to take your Fetch skills to the next level. Let‘s get started!
What is the Fetch API?
The Fetch API is a built-in JavaScript interface for making HTTP requests in the browser. It was introduced as an improvement over the older XMLHttpRequest API, with a more powerful and flexible feature set.
At its simplest, the Fetch API allows you to make an HTTP request to a URL and process the response. A basic example looks like this:
fetch(‘https://api.example.com/data‘)
.then(response => response.json())
.then(data => console.log(data));
Here we‘re making a GET request to https://api.example.com/data, parsing the response as JSON, and logging the resulting data to the console. Even in this basic example, you can see a couple key features of Fetch:
- It uses Promises, allowing for cleaner asynchronous code than callbacks
- It has built-in methods for parsing response data, like
.json()
But this just scratches the surface of what Fetch can do. Before we dive deeper, let‘s look at how Fetch improves on the older way of making requests in JavaScript.
Fetch vs XMLHttpRequest
Prior to Fetch, XMLHttpRequest (XHR) was the primary way to make HTTP requests in JavaScript. Here‘s what that looks like:
const xhr = new XMLHttpRequest();
xhr.open(‘GET‘, ‘https://api.example.com/data‘);
xhr.onload = () => {
if (xhr.status === 200) {
console.log(JSON.parse(xhr.response));
} else {
console.error(‘Request failed. Returned status of ‘ + xhr.status);
}
};
xhr.send();
You can see this is significantly more verbose and harder to read than the Fetch equivalent. The Fetch version takes 3 lines while the XHR version requires 12!
But it‘s not just about the syntax. Fetch has a number of key advantages over XHR:
- Fetch uses Promises, avoiding callback hell and making it easy to chain requests
- Fetch has more readable syntax, with separate methods for request and response objects
- Fetch has built-in support for parsing response data as text, JSON, blob objects, and more
- Fetch aborts a request with the AbortController API, rather than the more esoteric timeout approach
- Fetch has better error handling, raising errors for network failures, unlike XHR which just returns a status
In general, Fetch provides a much more modern, friendly and powerful interface for making requests. Now let‘s dive into the details of how to use it.
How to Use the Fetch API
Making GET Requests
To make a GET request with Fetch, you simply call fetch() with the URL you want to request:
fetch(‘https://api.example.com/data‘)
.then(response => response.json())
.then(data => console.log(data));
By default, fetch() makes a GET request. So this will make a GET request to https://api.example.com/data, parse the JSON response, and log it to the console.
Making POST Requests
To make a POST request with Fetch, you need to pass a second argument to fetch() with the request details:
fetch(‘https://api.example.com/data‘, {
method: ‘POST‘,
headers: {
‘Content-Type‘: ‘application/json‘
},
body: JSON.stringify({ name: ‘John‘, age: 30 })
})
.then(response => response.json())
.then(data => console.log(data));
Here we‘re setting the request method to POST, setting a JSON Content-Type header, and passing an object as JSON in the request body.
You can use this same options argument to set other request details like headers, credentials, caching directives and more. See the Fetch API documentation for the full set of options.
Handling Responses
When you make a fetch() request, the promise resolves to a Response object representing the response to your request. This Response has properties and methods to inspect and extract data from the response:
fetch(‘https://api.example.com/data‘)
.then(response => {
console.log(response.status);
console.log(response.statusText);
console.log(response.headers.get(‘Content-Type‘));
return response.json();
})
.then(data => console.log(data));
Here we‘re logging the response status code, status text, and Content-Type header, before parsing the response as JSON.
The Response object has methods to parse the response in various formats:
response.text()– plain textresponse.json()– parsed JSON objectresponse.blob()– raw data as a Blob objectresponse.formData()– data encoded asmultipart/form-dataresponse.arrayBuffer()– low-level representation of binary data
Handling Errors
Fetch will reject its promise if there‘s a network error (e.g. offline, host unreachable), but not for error HTTP status codes like 404 or 500. To properly handle errors, you need to check if the response is successful in the first promise callback:
fetch(‘https://api.example.com/data‘)
.then(response => {
if (!response.ok) {
throw new Error(‘Network response was not OK‘);
}
return response.json();
})
.then(data => console.log(data))
.catch(error => console.error(‘Error:‘, error));
Here we‘re throwing an error if response.ok is false, which means the status code is not in the 200-299 range. We catch the error in a .catch() block at the end of the promise chain.
Advanced Fetch Techniques
Using async/await
Promises are nice, but async/await syntax makes asynchronous code even easier to write and reason about. Here‘s how you can use async/await with Fetch:
async function getData() {
try {
const response = await fetch(‘https://api.example.com/data‘);
if (!response.ok) {
throw new Error(‘Network response was not OK‘);
}
const data = await response.json();
console.log(data);
} catch (error) {
console.error(‘Error:‘, error);
}
}
getData();
With async/await, we can write our asynchronous code in a way that looks more like synchronous code. We use await to wait for promises to resolve, and wrap the whole thing in a try/catch block to handle errors.
Aborting Requests
Sometimes you may want to abort a fetch() request before it completes – for example if the user navigates away from the page, or you want to implement a request timeout. You can abort a Fetch request with an AbortController.
Here‘s an example of setting a 5 second timeout on a request:
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000);
fetch(‘https://api.example.com/data‘, { signal: controller.signal })
.then(response => response.json())
.then(data => console.log(data))
.catch(error => {
if (error.name === ‘AbortError‘) {
console.error(‘Request aborted‘);
} else {
console.error(‘Error:‘, error);
}
});
We create an AbortController, and set a 5 second timeout to call its abort() method. We pass the AbortController‘s signal property in the fetch() options. If the request takes longer than 5 seconds, it will be aborted, rejecting the promise with an AbortError.
Fetch vs Other HTTP Libraries
While Fetch is excellent, it‘s not the only way to make HTTP requests in JavaScript. There are popular libraries like Axios and SuperAgent that provide an alternative to Fetch.
Compared to Fetch, these libraries can offer some additional features like:
- Automatic transforms for JSON data
- Client side support for protecting against XSRF
- Shorthand methods for different HTTP verbs (
axios.get(),axios.post()etc) - Built-in timeouts
- Progress callbacks for tracking upload/download progress
However, Fetch has some advantages over these libraries:
- Fetch has a cleaner, more modern API than some older libraries like SuperAgent
- As a built-in browser API, Fetch doesn‘t require installing an additional library, reducing bundle size and dependencies
- Fetch follows the standard WHATWG Fetch specification, while third-party libraries can be less standardized
- Fetch integrates better with modern browser features like Service Workers, allowing things like offline support and background sync
Overall, Fetch is an excellent choice for most use cases. Third-party libraries can be useful if you need some of their specific features, but for most projects Fetch will be a simpler and lighter weight choice.
Fetch API Best Practices
To get the most out of Fetch, there are some best practices to keep in mind:
- Always check if the response was successful in the first
.then()block, to properly handle HTTP errors - Use async/await syntax for more readable asynchronous code
- Remember that
fetch()won‘t reject on HTTP error status, only network failures. Handle both cases appropriately - Cancel no longer needed requests with an
AbortControllerto free up network resources - Set request timeouts to prevent hanging requests
- Polyfill the Fetch API if you need to support older browsers
- Use the appropriate response parsing method (
json(),text()etc) for your data type - Catch errors at the end of your promise chains
- Consider a retry strategy for failed requests, with a limit to prevent infinite retries
- Use HTTPS for all requests to protect your users‘ data
Fetch API Use Cases
The Fetch API is incredibly versatile, and can handle almost any HTTP request scenario you‘d need in a web application. Some common use cases include:
- Retrieving data from a REST API to display in your UI
- Submitting form data to a server
- Loading additional content on scroll or button click
- Sending analytics data to track user behavior
- Making AJAX requests in a Single Page Application
- Implementing typeahead search suggestions
- Lazy loading images as the user scrolls
- Pinging a health check endpoint to monitor server status
Whatever your use case, Fetch provides a clean, modern interface for making HTTP requests in the browser. Its support for Promises and async/await makes it easy to integrate with the rest of your JavaScript code.
Conclusion
In this guide, we‘ve covered everything you need to know to start using the Fetch API in your projects. We‘ve explained what Fetch is, how it improves on older techniques like XMLHttpRequest, and provided detailed examples of how to use it for common HTTP tasks.
You‘ve learned how to make GET, POST and other HTTP requests, handle responses and errors, use async/await for more readable code, cancel requests with AbortController, and more. We‘ve also discussed some best practices to keep in mind, and explored how Fetch compares to other HTTP request libraries.
With the knowledge you‘ve gained from this guide, you‘re ready to start using Fetch in your own projects. Whether you‘re building a simple prototype or a complex web application, Fetch provides a powerful and flexible way to handle HTTP requests in JavaScript.
