Module: Browser APIs

Fetch/HTTP

JavaScript Essentials: Browser APIs - Fetch/HTTP

This document covers the Fetch API in JavaScript, a modern interface for making network requests. It's a replacement for the older XMLHttpRequest (XHR) object, offering a more powerful and flexible way to interact with web servers.

What is Fetch?

The Fetch API provides an interface for fetching resources (like data from a server). It's built on Promises, which makes asynchronous operations easier to handle. It allows you to:

  • Make HTTP requests: GET, POST, PUT, DELETE, etc.
  • Retrieve data: JSON, text, images, etc.
  • Send data: Form data, JSON payloads, etc.
  • Control request headers: Set authentication, content type, etc.
  • Handle errors: Gracefully manage network issues and server errors.

Basic Fetch Usage

The core of the Fetch API is the fetch() function.

fetch('https://jsonplaceholder.typicode.com/todos/1') // Replace with your API endpoint
  .then(response => {
    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }
    return response.json(); // Parse the response body as JSON
  })
  .then(data => {
    console.log(data); // Process the JSON data
  })
  .catch(error => {
    console.error('Fetch error:', error); // Handle any errors
  });

Explanation:

  1. fetch('https://jsonplaceholder.typicode.com/todos/1'): Initiates a GET request to the specified URL. fetch() returns a Promise.
  2. .then(response => ...): This is the first then block. It's executed when the request is successful (even if the HTTP status code is an error like 404). The response object contains information about the response, including headers, status code, and the response body.
    • if (!response.ok): Checks if the HTTP status code indicates success (200-299). If not, it throws an error. This is crucial for handling server-side errors.
    • return response.json(): Parses the response body as JSON. This also returns a Promise. Other methods include:
      • response.text(): Parses the response body as text.
      • response.blob(): Parses the response body as a Blob (for binary data like images).
      • response.formData(): Parses the response body as FormData.
      • response.arrayBuffer(): Parses the response body as an ArrayBuffer.
  3. .then(data => ...): This second then block is executed when the JSON parsing is complete. The data variable contains the parsed JSON object.
  4. .catch(error => ...): This block is executed if any error occurs during the fetch process (network error, invalid URL, JSON parsing error, or the error thrown in the first then block).

Making Different Types of Requests

POST Request

fetch('https://jsonplaceholder.typicode.com/posts', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json' // Important for sending JSON data
  },
  body: JSON.stringify({
    title: 'foo',
    body: 'bar',
    userId: 1,
  })
})
  .then(response => {
    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }
    return response.json();
  })
  .then(data => {
    console.log('Success:', data);
  })
  .catch(error => {
    console.error('Error:', error);
  });

Key points:

  • method: 'POST': Specifies the HTTP method.
  • headers: An object containing request headers. Content-Type is essential when sending data.
  • body: The data to be sent in the request body. JSON.stringify() converts a JavaScript object into a JSON string.

PUT/DELETE Requests

Similar to POST, you specify the method and optionally include a body for PUT requests. DELETE requests typically don't have a body.

// PUT request
fetch('https://jsonplaceholder.typicode.com/posts/1', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    id: 1,
    title: 'updated title',
    body: 'updated body',
    userId: 1,
  })
})
  .then(...) // Handle response

// DELETE request
fetch('https://jsonplaceholder.typicode.com/posts/1', {
  method: 'DELETE'
})
  .then(...) // Handle response

Request Options

The second argument to fetch() is an options object. Here are some common options:

  • method: The HTTP method (GET, POST, PUT, DELETE, etc.). Defaults to 'GET'.
  • headers: An object containing request headers.
  • body: The request body (for POST, PUT, etc.).
  • mode: Controls how the request is made (e.g., cors, no-cors, same-origin). cors is the most common for cross-origin requests.
  • credentials: Controls whether cookies, authorization headers, and TLS client certificates are sent with the request. Options: include, same-origin, omit.
  • cache: Controls the caching behavior. Options: default, no-store, reload, no-cache, force-cache, only-if-cached.
  • redirect: Controls how redirects are handled. Options: follow (default), error, manual.

Handling Errors

Proper error handling is crucial. The fetch() API doesn't automatically reject the Promise for HTTP error status codes (like 404 or 500). You must check response.ok and throw an error if it's false.

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

Async/Await Syntax (Recommended)

Using async/await makes Fetch API code cleaner and more readable.

async function fetchData() {
  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('Fetch error:', error);
  }
}

fetchData();

Explanation:

  • async function fetchData(): Declares an asynchronous function.
  • await fetch(...): Pauses execution until the fetch() Promise resolves.
  • await response.json(): Pauses execution until the response.json() Promise resolves.
  • try...catch: Handles errors in a more structured way.

Resources

This provides a solid foundation for using the Fetch API in JavaScript. Remember to always handle errors and choose the appropriate request method and options for your specific needs. Using async/await is highly recommended for cleaner and more maintainable code.