What is the JavaScript Fetch API and How Can You Use It?

Introduction

The Fetch API marks a substantial shift in the way web applications conduct network queries. Fetch, introduced in current browsers as a more powerful and flexible alternative to XMLHttpRequest (XHR), makes it easier to communicate with servers and retrieve content over the network. This API is critical in the building of modern online applications, providing a more natural and efficient approach to handle asynchronous operations.

What is the Fetch API?

Fetch is a JavaScript API that offers a more comprehensive and extensible way to send HTTP requests. Unlike the earlier XMLHttpRequest, which was complex and often difficult to use, Fetch provides a simpler, more modern interface that works flawlessly with JavaScript’s Promise API. This integration simplifies the handling of asynchronous operations while also improving code readability and maintainability.

Fetch works by offering a global fetch() function that sends a network request. This function produces a Promise that resolves to the Response object, which is the response to the request. This response object gives developers access to response data, headers, and status, making it easier to work with network request outcomes.

Brief Overview

The Fetch API’s design is centered around the principles of simplicity and clarity. It allows developers to make requests and handle responses using modern JavaScript syntax and features. The API’s support for Promise objects means that developers can use familiar patterns for handling asynchronous operations, such as .then(), .catch(), and async/await.

The fetch() function takes two arguments: the URL of the resource to be fetched and an optional configuration object. The configuration object allows developers to specify various settings such as HTTP method, headers, body content, and mode. This flexibility makes Fetch suitable for a wide range of use cases, from simple GET requests to complex POST requests with custom headers and payloads.

Importance of Making Network Requests in Modern Web Applications

Network requests are essential for fetching and transmitting data to servers in modern web applications. These requests allow for dynamic content updates, real-time interactions, and integration with multiple web services and APIs. Web applications, for example, use network requests to retrieve user data, submit forms, change information without reloading the page, and communicate with third-party services.

The ability to efficiently process network requests is critical for offering a smooth and responsive user experience. Fetch API plays an important role in this by providing a dependable and simple mechanism to control these processes. Fetch enables developers to create effective and user-friendly applications by supporting contemporary JavaScript features and providing a straightforward syntax.

Why Use Fetch?

Comparison with XMLHttpRequest

The traditional XMLHttpRequest (XHR) object has been used for making network requests in JavaScript for many years. However, XHR has several limitations and drawbacks that Fetch addresses effectively.

Complex Syntax: XHR has a more complex and less readable syntax, involving multiple event handlers and state checks. This complexity can lead to verbose and difficult-to-maintain code.

Callback Hell: XHR often leads to nested callback functions, also known as “callback hell,” making asynchronous code harder to manage and debug.

Error Handling: Error handling in XHR is more cumbersome, requiring additional checks for different states and error conditions.

Response Handling: XHR requires manual parsing of response data, which can be cumbersome when dealing with different data formats.

Advantages of Using Fetch

Promises: One of Fetch’s key advantages is its usage of Promises. Promises are a more manageable way to handle asynchronous tasks than XHR’s callback-based approach. Promises allow developers to chain.The then() method handles successful answers, while the.catch() method handles problems. This pattern makes the code more legible and easy to understand.friendly.

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

With async/await, the Fetch API allows for even more straightforward asynchronous code

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

Cleaner Syntax: Fetch syntax is more current and less verbose than XHR. The configuration object supplied to fetch() allows you to easily set request parameters such as method, headers, and body content. This setup strategy produces cleaner, more maintainable code.

JavaScript
fetch('https://api.example.com/data', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ key: 'value' })
})
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));

Stream Handling: Fetch supports response streaming, allowing developers to handle large amounts of data more efficiently. With XHR, large responses could lead to performance issues or require additional handling to process in chunks. Fetch’s Response object provides methods like .body.getReader() for reading data in chunks, which is useful for streaming and handling large responses.

JavaScript
fetch('https://api.example.com/large-data')
  .then(response => {
    const reader = response.body.getReader();
    let decoder = new TextDecoder();
    let result = '';
    
    return reader.read().then(function processText({ done, value }) {
      if (done) {
        console.log('Stream finished.');
        return result;
      }
      result += decoder.decode(value, { stream: true });
      return reader.read().then(processText);
    });
  })
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));

Flexibility: Fetch provides greater flexibility with its configuration options, including modes (cors, no-cors, same-origin), credentials (include, same-origin, omit), and headers. This flexibility makes Fetch suitable for a wide range of scenarios, from simple GET requests to complex configurations for POST, PUT, or DELETE requests.

The Fetch API is a current innovation in handling network queries in web applications. Its usage of Promises, better syntax, and support for streaming make it an extremely useful tool for developers. Fetch, which replaces the previous XMLHttpRequest, simplifies asynchronous processes and improves code readability, which is critical for developing efficient and user-friendly web applications.

Getting Started with Fetch

The Fetch API is a modern and flexible approach to handle network requests in JavaScript. It is intended to be easier and more intuitive than the previous XMLHttpRequest (XHR) technique. This guide will walk you through the fundamentals of using Fetch, including syntax, making your first request, and handling responses.

Basic Syntax

fetch() Function

At the core of the Fetch API is the fetch() function. This function is used to initiate network requests and is available globally in modern browsers. It returns a Promise that resolves to the Response object representing the response to the request. The fetch() function accepts two main arguments:

  1. URL: A string representing the URL of the resource you want to fetch.
  2. Options (Optional): An object containing any custom settings or configurations for the request, such as HTTP method, headers, body, and mode.

Structure of a Simple Fetch Call

The most basic form of a fetch() call looks like this:

JavaScript
fetch(url)
  .then(response => {
    // Handle the response here
  })
  .catch(error => {
    // Handle any errors here
  });

In this structure:

  1. url is the address of the resource you want to fetch.
  2. The then() method handles the resolved Promise, which provides the Response object.
  3. The catch() method is used for handling any errors that occur during the request.

Here’s an example of a basic Fetch request:

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

Making Your First Fetch Request

To understand how to make a basic fetch request, let’s dive into an example of a simple GET request.

Example of a GET Request

A GET request is used to retrieve data from a specified URL. This is the most straightforward type of request and doesn’t require any special configurations beyond specifying the URL.

Here’s how to perform a GET request using Fetch:

JavaScript
fetch('https://api.example.com/data')
  .then(response => {
    if (!response.ok) {
      throw new Error('Network response was not ok ' + response.statusText);
    }
    return response.json(); // Parse the JSON from the response
  })
  .then(data => {
    console.log(data); // Handle the data from the response
  })
  .catch(error => {
    console.error('There has been a problem with your fetch operation:', error);
  });

In this example:

  1. Checking the Response: We first check if the response status is OK (status code in the range 200–299). If not, we throw an error.
  2. Parsing the Response: We call response.json() to parse the JSON data from the response. The json() method returns a Promise that resolves with the result of parsing the response body text as JSON.
  3. Handling the Data: The data is then available in the then() method, where you can handle it as needed.

Handling the Response

Handling the response correctly is crucial to ensure that your application behaves as expected. The Response object provides several methods for accessing the response data, including:

  1. .json(): Parses the response body as JSON and returns a Promise that resolves with the result.
  2. .text(): Returns the response body as a string.
  3. .blob(): Returns the response body as a Blob object, useful for handling binary data.
  4. .formData(): Returns the response body as FormData, which is useful for form submissions.

Here’s a more detailed example demonstrating different ways to handle the response:

JavaScript
fetch('https://api.example.com/data')
  .then(response => {
    if (!response.ok) {
      throw new Error('Network response was not ok ' + response.statusText);
    }
    // Choose the appropriate method based on the response type
    return response.json(); // or response.text(), response.blob(), response.formData()
  })
  .then(data => {
    console.log(data); // Handle the parsed data here
  })
  .catch(error => {
    console.error('There has been a problem with your fetch operation:', error);
  });

Advanced Usage

In addition to basic GET requests, the Fetch API supports various other configurations for more complex scenarios. You can customize the request method, headers, and body to suit different needs.

Configuring the Request

Here’s how to use the fetch() function with additional options:

JavaScript
fetch('https://api.example.com/data', {
  method: 'POST', // Specify the HTTP method (GET, POST, PUT, DELETE, etc.)
  headers: {
    'Content-Type': 'application/json' // Set request headers
  },
  body: JSON.stringify({ key: 'value' }) // Provide request body (for POST/PUT requests)
})
  .then(response => {
    if (!response.ok) {
      throw new Error('Network response was not ok ' + response.statusText);
    }
    return response.json();
  })
  .then(data => {
    console.log(data);
  })
  .catch(error => {
    console.error('There has been a problem with your fetch operation:', error);
  });

Common Configurations:

  1. method: The HTTP method to use (e.g., GET, POST, PUT, DELETE).
  2. headers: An object representing the request headers.
  3. body: The body of the request, which must be a string for most methods (e.g., JSON.stringify(data) for POST requests).

Handling Errors

Error management is an important aspect of working with Fetch. While the catch() block addresses network issues, you must also manage HTTP errors by inspecting the Response object’s ok field, which indicates if the request was successful.

JavaScript
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);
  });

In this example, if the response status code indicates an error (e.g., 404 Not Found or 500 Internal Server Error), we throw an error with the status code, which will be caught in the catch() block.

Handling Different Data Formats

Depending on the content type of the response, you might need to handle different data formats:

  1. JSON: Use response.json().
  2. Text: Use response.text().
  3. Blob: Use response.blob().
  4. FormData: Use response.formData().

Each of these methods returns a Promise that resolves with the parsed data. For example, if you expect a text response, you would use:

JavaScript
fetch('https://api.example.com/data')
  .then(response => response.text())
  .then(text => {
    console.log(text); // Handle the text response
  })
  .catch(error => {
    console.error('Fetch error:', error);
  });

Understanding the Fetch Response

When you make a request with the Fetch API, you will receive a Response object. This object holds all information about the server’s answer. Here’s an overview of the Response object’s attributes and methods, as well as how to use JSON data from it.

Response Object

Properties of the Response

  1. status
    • Description: The status code of the response. It’s a number representing the HTTP status code.
    • Example: 200 for a successful request, 404 for “Not Found”, 500 for “Internal Server Error”.
  2. statusText
    • Description: A string describing the status code. It’s a textual representation of the status.
    • Example: "OK" for a 200 status, "Not Found" for a 404 status.
  3. headers
    • Description: An instance of the Headers object representing the response headers. It provides methods to access headers.
    • Example: response.headers.get('Content-Type') to get the value of the Content-Type header.
  4. url
    • Description: The URL of the response.
    • Example: "https://api.example.com/data"
  5. ok
    • Description: A boolean indicating if the response status code is in the range 200-299 (inclusive). It’s a quick way to check if the request was successful.
    • Example: true if status code is 200, false if status code is 404.

Methods to Handle the Response Body

.json()

Description: Parses the response body as JSON and returns a promise that resolves with the result. Use this method if you expect the response to be in JSON format.

Example

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

.text()

Description: Reads the response body as text and returns a promise that resolves with the text. Use this method if the response is plain text.

Example

JavaScript
fetch('https://api.example.com/text')
  .then(response => response.text())
  .then(text => console.log(text))
  .catch(error => console.error('Error:', error));

.blob()

Description: Reads the response body as a Blob object, which can be useful for handling binary data like images or files.

Example

JavaScript
fetch('https://api.example.com/image')
  .then(response => response.blob())
  .then(blob => {
    const url = URL.createObjectURL(blob);
    document.querySelector('img').src = url;
  })
  .catch(error => console.error('Error:', error));

Handling JSON Data

When you expect the response to be JSON, you will use the .json() method to parse it. Here’s how you can work with JSON data:

1 Parsing JSON from the Response

When you call response.json(), it returns a promise that resolves with the parsed JSON object. This makes it easy to work with the data in JavaScript.

2 Example of Working with JSON Data

Suppose you’re fetching user data from an API. Here’s an example:

JavaScript
fetch('https://api.example.com/users')
  .then(response => {
    if (!response.ok) {
      throw new Error('Network response was not ok ' + response.statusText);
    }
    return response.json();
  })
  .then(users => {
    // Process the JSON data
    users.forEach(user => {
      console.log(`Name: ${user.name}, Email: ${user.email}`);
    });
  })
  .catch(error => console.error('There was a problem with your fetch operation:', error));

In this example:

  1. We first check if the response is ok using response.ok.
  2. We then parse the response body as JSON using response.json().
  3. Finally, we process the JSON data, which is an array of user objects, and log each user’s name and email.

Error Handling in Fetch API

Error handling is an important component of dealing with APIs since it ensures that your application can gracefully handle any errors that may emerge during data retrieval or communication. When utilizing the Fetch API, you must handle both network and HTTP problems effectively. Here’s how to handle these errors:

1 Handling Network Errors

Network problems can arise for a variety of reasons, including network issues, server downtime, and misconfigured URLs. Since the Fetch API only rejects the promise on network failures, resolving these errors entails:

a. Checking for Errors in the Fetch Response

The Fetch API does not reject the promise on HTTP error statuses (e.g., 404 or 500). It only rejects on network errors, such as when the server is unreachable. This means you need to manually check the response status to handle HTTP errors.

JavaScript
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('Network error:', error);
  });

b. Using .catch() to Handle Promise Rejections

The .catch() method is used to handle promise rejections, which include network errors or any errors thrown in the .then() block. This ensures that any issues encountered during the fetch operation are captured and dealt with appropriately.

JavaScript
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);
  });

2 Handling HTTP Errors

HTTP errors are issues caused by server responses with error codes such as 404 (Not Found) or 500 (Internal Server Error). The Fetch API does not regard these replies as errors by default, so you must manually check the status and handle them appropriately.

a. Checking the Response Status

To handle HTTP errors, you should inspect the response.ok property. If response.ok is false, this indicates that the response status is outside the range of 2xx. You can then throw an error with a descriptive message.

JavaScript
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('HTTP error:', error);
  });

b. Example of Throwing and Catching HTTP Errors:

Here’s an example of how you can throw and catch HTTP errors in practice

JavaScript
fetch('https://api.example.com/data')
  .then(response => {
    if (!response.ok) {
      // Throwing an error if the response status is not OK
      throw new Error(`HTTP error! Status: ${response.status}`);
    }
    return response.json();
  })
  .then(data => {
    // Process the data if the fetch is successful
    console.log(data);
  })
  .catch(error => {
    // Handle network and HTTP errors here
    console.error('Error occurred:', error.message);
  });

In this example, if the server responds with a status code that indicates an error (e.g., 404 or 500), an error is thrown and caught in the .catch() block. This ensures that your application can handle such errors gracefully and provide meaningful feedback to users.

Sending Data with Fetch

POST Requests

The fetch API in JavaScript offers a modern and powerful approach to interface with servers. One of its main features is the ability to submit data to a server using various HTTP protocols. In this section, we’ll look at POST requests, which are typically used to send data to a server for creation or updating resources.

1. Setting Up the Request Options

When making a POST request with fetch, you’ll need to configure several options. The fetch function accepts two arguments: the URL to which the request is sent and an options object that specifies the request details.

Here’s a breakdown of the key options you’ll configure for a POST request:

  1. method: This specifies the HTTP method to use. For POST requests, this should be set to "POST".
  2. headers: This is where you define the headers for your request. Headers provide additional information about the request. Common headers for POST requests include Content-Type, which specifies the media type of the resource being sent.
  3. body: This is the actual data you want to send in the request. For POST requests, this is usually a JSON string or form data.

Example of Sending JSON Data in a POST Request

Let’s say you have a server endpoint that accepts user registration data. You can use fetch to send this data as follows:

JavaScript
const url = 'https://example.com/api/register';
const userData = {
    username: 'newuser',
    password: 'password123',
    email: '[email protected]'
};

fetch(url, {
    method: 'POST', // Specify the HTTP method
    headers: {
        'Content-Type': 'application/json', // Specify that we're sending JSON data
    },
    body: JSON.stringify(userData) // Convert the JavaScript object to a JSON string
})
.then(response => response.json()) // Parse the JSON response
.then(data => {
    console.log('Success:', data);
})
.catch((error) => {
    console.error('Error:', error);
});

Explanation:

  1. URL: The endpoint to which the request is sent.
  2. method: Set to "POST" to indicate that data is being sent to the server.
  3. headers: Includes Content-Type: application/json to let the server know the data format.
  4. body: JSON.stringify(userData) converts the JavaScript object userData into a JSON string, which is then sent in the request body.

Handling JSON Data

When dealing with JSON data, you often need to parse the response as JSON. The response.json() method is used to parse the response body as JSON.

2. Other HTTP Methods

In addition to POST, there are several other HTTP methods that you can use with fetch. Each method serves a different purpose and is used in different scenarios.

PUT:

Used to update an existing resource or create a resource if it does not exist. The request typically contains the updated resource data.

Example Use Case: Updating a user profile.

JavaScript
const url = 'https://example.com/api/users/123';
const updatedUserData = {
    username: 'updateduser',
    email: '[email protected]'
};

fetch(url, {
    method: 'PUT',
    headers: {
        'Content-Type': 'application/json',
    },
    body: JSON.stringify(updatedUserData)
})
.then(response => response.json())
.then(data => {
    console.log('Updated:', data);
})
.catch((error) => {
    console.error('Error:', error);
});

Explanation: The PUT method is used here to update the user data for the user with ID 123.

DELETE

Used to delete a resource identified by a URL.

Example Use Case: Deleting a user account.

JavaScript
const url = 'https://example.com/api/users/123';

fetch(url, {
    method: 'DELETE'
})
.then(response => {
    if (response.ok) {
        console.log('User deleted successfully');
    } else {
        console.log('Failed to delete user');
    }
})
.catch((error) => {
    console.error('Error:', error);
});

Explanation: The DELETE method is used to remove the user with ID 123 from the server.

PATCH

Used to apply partial modifications to a resource. Unlike PUT, which typically replaces the entire resource, PATCH only updates the parts specified in the request.

Example Use Case: Updating a user’s email address.

JavaScript
const url = 'https://example.com/api/users/123';
const patchData = {
    email: '[email protected]'
};

fetch(url, {
    method: 'PATCH',
    headers: {
        'Content-Type': 'application/json',
    },
    body: JSON.stringify(patchData)
})
.then(response => response.json())
.then(data => {
    console.log('User email updated:', data);
})
.catch((error) => {
    console.error('Error:', error);
});

Explanation: The PATCH method is used here to only update the email address of the user with ID 123.

3. Summary

the fetch API provides a versatile and modern approach to making HTTP requests. When sending data with fetch, especially for POST requests, you need to carefully configure the request options, including the HTTP method, headers, and body. Each HTTP method (POST, PUT, DELETE, PATCH) has its own specific use cases and behaviors:

  1. POST: Used to send data to create or update resources.
  2. PUT: Used to update or create a resource.
  3. DELETE: Used to remove a resource.
  4. PATCH: Used to apply partial updates to a resource.

Advanced Fetch Usage

The Fetch API is a useful tool for handling network requests in modern JavaScript. While its basic usage is simple, learning advanced capabilities like adding headers, handling CORS (Cross-Origin Resource Sharing), and aborting requests can significantly improve the flexibility and performance of your code. In this section, we’ll delve into these complex topics, offering thorough explanations and practical examples.

1. Adding Headers

HTTP headers are required for transmitting info about the request or response. The Fetch API allows you to define headers to include a variety of information, such as content type, authentication tokens, and custom metadata.

Common Headers

  1. Content-Type: This header tells the server what type of data is being sent. For instance, when sending JSON data, you should specify application/json.
  2. Authorization: This header is used for passing authentication tokens or credentials. For example, Bearer <token> is commonly used for OAuth tokens.

Example of Adding Common Headers

Here’s how to use headers in a fetch request:

JavaScript
fetch('https://api.example.com/data', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer your-token-here'
    },
    body: JSON.stringify({ key: 'value' })
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));

In this example:

  1. The Content-Type header is set to application/json, indicating that the body contains JSON data.
  2. The Authorization header is used to pass a bearer token for authentication.

Adding Custom Headers

Custom headers are key-value pairs that you define for your specific needs. They can be useful for adding additional metadata or tracking information.

Example of Adding Custom Headers

JavaScript
fetch('https://api.example.com/track', {
    method: 'GET',
    headers: {
        'X-Custom-Header': 'CustomValue',
        'Authorization': 'Bearer your-token-here'
    }
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));

Here, we add a custom header X-Custom-Header with a value of CustomValue. This custom header can be used by the server for specific purposes.

2. Handling CORS (Cross-Origin Resource Sharing)

What is CORS?

CORS is a security feature implemented by browsers to prevent unauthorized access to resources from different origins (domains). It ensures that a web application running at one origin cannot make requests to a different origin unless explicitly allowed by the target server.

Example of Handling CORS Issues

Handling CORS typically involves configuring the server to include the appropriate headers in its responses. For example, the server needs to specify which origins are allowed to access its resources.

Server-Side Example (Node.js/Express):

JavaScript
const express = require('express');
const app = express();

app.use((req, res, next) => {
    res.header('Access-Control-Allow-Origin', '*'); // Allow all origins
    res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
    res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
    next();
});

app.get('/data', (req, res) => {
    res.json({ message: 'CORS is working!' });
});

app.listen(3000, () => console.log('Server running on port 3000'));

In this example, the server includes CORS headers to allow requests from any origin ('*'). It also specifies allowed methods and headers.

On the client side, no special handling is required; the Fetch API will automatically handle the CORS policy as long as the server is correctly configured.

3. Abort Requests

Aborting requests can be crucial for improving the performance and user experience of your application. The AbortController interface allows you to cancel fetch requests when they are no longer needed.

Using the AbortController to Cancel Requests

The AbortController provides a way to signal that a request should be canceled. This is particularly useful for handling scenarios where a request might be taking too long or when a user navigates away from a page.

Creating an AbortController:

JavaScript
const controller = new AbortController();
const signal = controller.signal;

fetch('https://api.example.com/long-request', {
    method: 'GET',
    signal: signal
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => {
    if (error.name === 'AbortError') {
        console.log('Request was aborted');
    } else {
        console.error('Error:', error);
    }
});

Example of Aborting a Fetch Request

You can abort a request by calling the abort() method on the AbortController instance. Here’s an example:

JavaScript
const controller = new AbortController();
const signal = controller.signal;

const fetchPromise = fetch('https://api.example.com/long-request', {
    method: 'GET',
    signal: signal
});

// Abort the request after 2 seconds
setTimeout(() => {
    controller.abort();
}, 2000);

fetchPromise
    .then(response => response.json())
    .then(data => console.log(data))
    .catch(error => {
        if (error.name === 'AbortError') {
            console.log('Request was aborted');
        } else {
            console.error('Error:', error);
        }
    });

In this example:

  1. The fetch request is initiated with an AbortController signal.
  2. The abort() method is called after 2 seconds, canceling the request.
  3. The catch block checks if the error is an AbortError and logs a specific message if the request was aborted.

Practical Examples

Fetching Data from JSONPlaceholder API

Objective: Fetch a list of users from the JSONPlaceholder API and display their names.

1. Basic Fetch Request

Here’s a simple example of how to fetch data from the JSONPlaceholder API using fetch.

JavaScript
// URL of the JSONPlaceholder API
const apiUrl = 'https://jsonplaceholder.typicode.com/users';

// Fetch the data
fetch(apiUrl)
  .then(response => {
    // Check if the response is OK
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }
    return response.json(); // Parse JSON from the response
  })
  .then(data => {
    // Handle the data from the API
    console.log('User data:', data);
    data.forEach(user => {
      console.log(`Name: ${user.name}`);
    });
  })
  .catch(error => {
    // Handle errors
    console.error('There was a problem with the fetch operation:', error);
  });

Explanation:

  1. fetch(apiUrl): Sends a GET request to the specified URL.
  2. .then(response => {...}): Handles the Response object. It checks if the response is successful with response.ok. If not, an error is thrown.
  3. response.json(): Parses the response body as JSON.
  4. .then(data => {...}): Handles the parsed data. Here, we log the data and iterate over the array of users to print their names.
  5. .catch(error => {...}): Catches and handles any errors that occur during the fetch operation.

2. Fetching with Error Handling

Let’s enhance the previous example by adding specific error handling for both network and HTTP errors.

JavaScript
const apiUrl = 'https://jsonplaceholder.typicode.com/users';

async function fetchUsers() {
  try {
    const response = await fetch(apiUrl);

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

    const data = await response.json();
    console.log('User data:', data);
    data.forEach(user => {
      console.log(`Name: ${user.name}`);
    });
  } catch (error) {
    console.error('Error fetching data:', error);
  }
}

fetchUsers();

Explanation:

  1. async function fetchUsers() {...}: Defines an asynchronous function to handle the fetch operation.
  2. await fetch(apiUrl): Waits for the fetch request to complete.
  3. if (!response.ok) { ... }: Checks if the response status is OK. If not, it throws an error with the status code.
  4. const data = await response.json(): Waits for the JSON parsing to complete.
  5. .catch(error => {...}): Catches and logs any errors that occur.

Form Submission with Fetch

Example of Using Fetch to Submit Form Data

Submitting form data using Fetch is straightforward. Here’s a step-by-step guide:

HTML Form Example

HTML
<form id="myForm">
  <label for="name">Name:</label>
  <input type="text" id="name" name="name" required>
  <label for="email">Email:</label>
  <input type="email" id="email" name="email" required>
  <button type="submit">Submit</button>
</form>

JavaScript Code to Handle Form Submission

JavaScript
document.getElementById('myForm').addEventListener('submit', function(event) {
  event.preventDefault(); // Prevent the default form submission

  // Create a FormData object from the form
  const formData = new FormData(this);

  // Use Fetch to send the form data
  fetch('https://example.com/submit', {
    method: 'POST',
    body: formData
  })
  .then(response => response.json()) // Parse JSON response
  .then(data => {
    console.log('Success:', data); // Handle success
  })
  .catch(error => {
    console.error('Error:', error); // Handle errors
  });
});

Handling Form Data and Response

1. FormData Object

  • Creating FormData: The FormData object automatically captures form field values. It is useful for submitting data in a POST request.
  • Accessing FormData: You can use methods like .get() and .entries() to access form data.

2. Sending the Form Data

  1. Content-Type Header: When using FormData, you don’t need to set the Content-Type header manually. The browser sets it for you as multipart/form-data.

Handling the Response

  1. Parsing Response: Use .json(), .text(), or .blob() methods based on the response format.
  2. Error Handling: Always include error handling to catch any issues with the request or response.

JavaScript
fetch('https://example.com/submit', {
  method: 'POST',
  body: formData
})
.then(response => {
  if (!response.ok) {
    throw new Error('Network response was not ok');
  }
  return response.json();
})
.then(data => {
  console.log('Success:', data);
})
.catch(error => {
  console.error('Error:', error);
});

Considerations

Form Validation:

Ensure that client-side validation (e.g., required fields) is handled before sending data.

Security:

Consider implementing server-side validation and sanitization to protect against malicious input.

User Feedback:

Provide user feedback (e.g., success messages, error alerts) to enhance the user experience.

JSON Tutorial

CSSFetch APIHTMLJavascript

Recent Blogs

Mastering Local Storage in Web Development: 8 Practical Examples, From Novice to Expert! #3

Local storage is a useful web development tool that enables developers to save data within the user’s browser. In this article, we’ll look at different features of local storage, starting with beginner-level examples and continuing to more complex techniques. By the end of this guide, you will have a basic understanding of how to successfully […]

CSSHTMLJavascriptWeb Storage

Top 5 Essential Tips for Writing Better CSS #1

Introduction CSS (Cascading Style Sheets) is the foundation of web design, responsible for the visual appearance of a website. Writing efficient and clean CSS code is essential for developing maintainable and scalable web projects. Whether you are a newbie or an experienced developer, these ten tips can help you improve your CSS skills and produce […]

CSSHTMLWeb design

Exploring the :has() Selector in CSS: A Comprehensive Guide with 9 Examples

CSS has progressed greatly over time, introducing a number of advanced selectors that improve the ability to style web pages with precision and flexibility. One of the most recent additions to the CSS selector is the :has() pseudo-class. This blog will go over the details of the :has() selector, including its usage, benefits, and practical […]

CSSHTMLWeb design

CSS Variables : The Key to Empowering Your Stylesheets #1

CSS Variables – The ability to produce dynamic and customisable design effects is essential in the field of web development. Custom properties, or CSS variables, provide a way to this realm which allows developers to specify reusable values within stylesheets and modify them dynamically during runtime.

CSSHTMLJavascriptWeb design