Using the Fetch API (JavaScript)
Learn Using the Fetch API (JavaScript) step by step with clear examples and exercises.
Why This Matters
The Fetch API plays a significant role in modern web development as it enables JavaScript to interact with servers for data fetching and manipulation, which is a fundamental task in web applications. Unlike its predecessor XMLHttpRequest (XHR), the Fetch API offers several advantages such as being promise-based, integrating well with features of the modern web like service workers and Cross-Origin Resource Sharing (CORS). This lesson will guide you through using the Fetch API to fetch data from servers, check response statuses, and extract data in various formats.
Prerequisites
To follow this lesson, you should have a solid understanding of:
- Basic JavaScript concepts such as variables, functions, control structures (loops, conditional statements), and asynchronous programming using Promises.
- Familiarity with HTTP methods, response status codes, request headers, and content types is also beneficial.
- A basic understanding of the Document Object Model (DOM) and how to manipulate it will be helpful when working with responses that need to be displayed on a webpage.
Core Concept
Fetch API Basics
The fetch() function is a global function available in both the window and worker contexts. To make a request, you call fetch(), passing it a Request object or a string containing the URL to fetch, along with an optional argument to configure the request. The fetch() function returns a Promise which is fulfilled with a Response object representing the server's response.
fetch('https://example.com')
.then(response => {
// Handle the response here
})
.catch(error => console.log(error));
Request Configuration
In addition to a URL, you can configure the request by passing an options object as the second argument to fetch(). Commonly used configuration properties include:
method: The HTTP method for the request (e.g., GET, POST, PUT, DELETE)headers: AHeadersobject containing key-value pairs representing request headersbody: The body of the request (for methods like POST and PUT), typically a string or aReadableStreamcredentials: An optional parameter to control whether cookies are sent with the request (default is "same-origin")redirect: A configuration option that specifies how redirects should be handled during the fetch process (e.g., 'follow', 'manual')referrerPolicy: A policy that determines how the referrer header will be sent with the requestintegrity: An optional Subresource Integrity (SRI) check for the response bodykeepalive: A boolean value indicating whether to keep the connection open for future requests (default is false)timeout: The maximum time in milliseconds that the fetch operation should take before being aborted
const myHeaders = new Headers();
myHeaders.append('Authorization', 'Bearer my_token');
fetch('https://example.com', {
method: 'POST',
headers: myHeaders,
body: JSON.stringify({ key: 'value' }),
credentials: 'include', // Send cookies with the request
redirect: 'follow', // Follow redirects during the fetch process
referrerPolicy: 'no-referrer', // Do not send a referrer header
integrity: 'sha256-hash_here', // Subresource Integrity check for the response body
keepalive: true, // Keep the connection open for future requests
timeout: 10000 // Set a timeout of 10 seconds
})
.then(response => {
// Handle the response here
});
Response Handling
Once you have a Response object, you can check its status and extract data in various formats using appropriate methods on the response. Commonly used methods include:
text(): Returns the response body as aPromisethat resolves with a stringjson(): Returns the response body as aPromisethat resolves with a JavaScript object if the content type is JSONarrayBuffer(): Returns the response body as anArrayBuffer(a typed array of raw binary data)blob(): Returns the response body as aBlobobject, which can be used for file downloads or manipulationformData(): Returns the response body as aFormDataobject if the content type is multipart/form-dataheaders: Access the response headers as aHeadersobjectok: A boolean indicating whether the HTTP status code is in the 2xx (success) rangestatus: The HTTP status code of the responsestatusText: A human-readable description of the HTTP status codeurl: The URL that was requested
fetch('https://example.com')
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.text();
})
.then(data => console.log(data))
.catch(error => console.log(error));
CORS and Fetch
Fetch will automatically handle Cross-Origin Resource Sharing (CORS) for you, but if the server does not support CORS or if you are making a request to a different domain, port, or protocol, you may encounter issues. In such cases, you can use various strategies like JSONP, proxy servers, or CORS Anywhere to work around these restrictions.
Worked Example
Let's create a more complex example that fetches data from an API, checks for authentication, and displays it in the browser:
- First, we define an
asyncfunction to handle fetching, authenticating, and displaying the data.
async function fetchData() {
try {
const response = await fetch('https://example.com/api/data');
if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`);
const data = await response.json();
// Check for authentication
if (data.requiresAuthentication && !isAuthenticated()) {
alert('You must be logged in to access this data.');
return;
}
displayData(data);
} catch (error) {
console.log(error);
}
}
- Next, we define a function to check if the user is authenticated:
function isAuthenticated() {
// Check for authentication logic here, e.g., by checking local storage or cookies
return true;
}
- We then define a function to display the fetched data in the HTML:
function displayData(data) {
const postsContainer = document.getElementById('posts');
postsContainer.innerHTML = ''; // Clear the container before updating it
data.forEach(item => {
const postElement = document.createElement('div');
postElement.innerHTML = `
<h2>${item.title}</h2>
<p>${item.body}</p>
`;
postsContainer.appendChild(postElement);
});
}
- Finally, we call the
fetchData()function to start the process:
fetchData();
Common Mistakes
- Forgetting to check if the response is ok before handling it: Always check the response status using
response.ok. If the status is not 2xx (success), throw an error or handle the situation appropriately. - Not handling errors properly: Make sure you catch and log any errors that occur during the fetch process.
- Ignoring the CORS policy: Fetch will automatically handle Cross-Origin Resource Sharing (CORS) for you, but if the server does not support CORS or if you are making a request to a different domain, port, or protocol, you may encounter issues.
Common Mistakes - Additional Considerations
- Not handling response headers: Response headers can provide valuable information about the server's response, such as content type, cache control, and authentication tokens. You can access these headers using the
headersproperty of theResponseobject. - Misusing or ignoring the
response.statusTextproperty: This property provides a human-readable description of the HTTP status code. While it's not always necessary to use this property, it can be helpful in debugging and understanding response errors. - Not handling the
response.urlproperty: This property contains the URL that was requested. It can be useful for logging or debugging purposes. - Not properly handling asynchronous operations: When working with multiple concurrent requests or asynchronous functions, make sure to use
async/await,Promise.all(), or other techniques to manage the flow of your code effectively.
Practice Questions
- Modify the example above to fetch data from an API that requires authentication by setting the
Authorizationheader in your request and checking for authentication before displaying the data. - Write a function that fetches user data based on a provided ID and returns it as a JavaScript object. The API endpoint for this data is:
https://jsonplaceholder.typicode.com/users/{id}. - Write a function that sends a POST request to an API with JSON data in the body. The API endpoint for this data is:
https://my-api.com/data. - Write a function that fetches the headers of a response and logs them to the console.
- What strategies can you use to work around CORS restrictions when making requests with Fetch?
- How would you handle multiple concurrent requests using Fetch?
- How can you cancel a request using Fetch?
- How would you set up basic authentication (username and password) for an API request using Fetch?
- What is the difference between
fetch()andXMLHttpRequest(XHR)? Discuss their advantages and disadvantages. - Explain how Subresource Integrity (SRI) works with Fetch and why it's important.
FAQ
Q1: What happens if I make a request and the server takes a long time to respond?
A1: Fetch will automatically handle timeouts based on your browser's network settings, but you can also set custom timeout values using the timeout configuration option in your fetch call.
Q2: Can I use fetch for file uploads or downloads?
A2: Yes! You can use the FormData API to create a multipart form and send it as the body of a POST request, which is commonly used for file uploads. For downloading files, you can set the response type to blob and then create an BlobURL or DownloadLink to access the file.
Q3: How do I handle multiple concurrent requests using Fetch?
A3: You can make multiple concurrent requests by creating separate fetch calls and handling them individually, or you can use Promise.all() to wait for all promises to resolve before continuing with your code.
Q4: Can I cancel a request using Fetch?
A4: Yes! You can cancel a request by calling the abort() method on the Request object before it is sent. This is useful when the user navigates away from the page or cancels an action that initiated the request.
Q5: How would you set up basic authentication (username and password) for an API request using Fetch?
A5: To set up basic authentication, you can include your username and password in the Authorization header of your fetch call as a Base64-encoded string. Here's how to create that string:
const user = 'username';
const pass = 'password';
const base64Creds = btoa(`${user}:${pass}`);
Then, include this base64Creds string in your fetch call's headers:
fetch('https://example.com/api', {
method: 'GET',
headers: {
Authorization: `Basic ${base64Creds}`
}
})
// ...
Q6: What is the difference between fetch() and XMLHttpRequest (XHR)? Discuss their advantages and disadvantages.
A6: Both fetch() and XMLHttpRequest (XHR) are used to make HTTP requests in JavaScript, but they have some differences:
- Syntax:
fetch()is a modern, promise-based API, while XHR uses callbacks or promises. - Integration: Fetch integrates better with features of the modern web like service workers and Cross-Origin Resource Sharing (CORS), whereas XHR has been around for a longer time and may be more widely supported in older browsers.
- Error handling: Fetch provides a cleaner error handling mechanism using try/catch blocks, while XHR requires explicit error handling through callbacks or event listeners.
- Response types: Fetch supports various response types out of the box (text, json, arrayBuffer, blob), whereas XHR may require additional setup for certain response types.
Advantages of fetch():
- Modern and promise-based syntax