Fetch api in javascript

Updated on

To effectively harness the Fetch API in JavaScript, here are the detailed steps for making network requests:

👉 Skip the hassle and get the ready to use 100% working script (Link in the comments section of the YouTube Video) (Latest test 31/05/2025)

The Fetch API is a powerful, modern interface for fetching resources across the network.

0.0
0.0 out of 5 stars (based on 0 reviews)
Excellent0%
Very good0%
Average0%
Poor0%
Terrible0%

There are no reviews yet. Be the first one to write one.

Amazon.com: Check Amazon for Fetch api in
Latest Discussions & Reviews:

It’s promise-based, making it far more flexible and robust than older methods like XMLHttpRequest. Think of it as your primary tool for interacting with APIs, pulling data, or sending information to a server.

  1. Basic GET Request:

    To retrieve data, simply call fetch with the URL.

    fetch'https://api.example.com/data'
    
    
     .thenresponse => response.json // Parse the JSON response
    
    
     .thendata => console.logdata    // Work with the data
    
    
     .catcherror => console.error'Error:', error. // Handle any errors
    

    This sequence is crucial: fetch returns a Promise that resolves to the Response object.

You then use response.json or response.text, response.blob, etc. to parse the body, which itself returns another Promise that resolves to the actual data.

  1. Handling Network Errors vs. HTTP Errors:

    The catch block only handles network-related issues e.g., no internet, DNS failure. For HTTP errors like 404 Not Found or 500 Internal Server Error, you need to check response.ok a boolean indicating a successful HTTP status code, 200-299 or response.status.
    fetch’https://api.example.com/nonexistent
    .thenresponse => {
    if !response.ok {

    throw new ErrorHTTP error! status: ${response.status}.
    }
    return response.json.
    }
    .thendata => console.logdata

    .catcherror => console.error’Fetch error:’, error.

  2. Making POST Requests Sending Data:

    When sending data, you need to provide a second argument to fetch: an options object.

This object specifies the method, headers, and body.
const postData = {
name: ‘John Doe’,
occupation: ‘Developer’
}.

 fetch'https://api.example.com/submit', {
   method: 'POST', // or 'PUT', 'DELETE', etc.
   headers: {


    'Content-Type': 'application/json', // Crucial for JSON data


    'Accept': 'application/json'        // Indicate expected response type
   },


  body: JSON.stringifypostData // Convert JavaScript object to JSON string
 }
   .thenresponse => response.json
   .thendata => console.log'Success:', data


  .catcherror => console.error'Error:', error.


Always remember to `JSON.stringify` your JavaScript object when sending JSON data in the request `body`.
  1. Sending Form Data e.g., File Uploads:

    For sending data like form submissions or files, FormData is your friend.
    const formData = new FormData.
    formData.append’username’, ‘Alice’.

    FormData.append’profilePicture’, myFile, ‘profile.jpg’. // myFile is a File object

    fetch’https://api.example.com/upload‘, {
    method: ‘POST’,

    body: formData // Fetch automatically sets Content-Type header for FormData

    .thendata => console.log’Upload success:’, data

    .catcherror => console.error’Upload error:’, error.
    Crucially, do not set the Content-Type header manually when using FormData. fetch handles this automatically, including the necessary boundary string.

  2. Cancelling a Fetch Request:

    While fetch itself doesn’t have a direct cancel method, you can use AbortController to achieve this.
    const controller = new AbortController.
    const signal = controller.signal.

    Fetch’https://api.example.com/long-running-task‘, { signal }

    .thendata => console.log’Task completed:’, data
    .catcherror => {
    if error.name === ‘AbortError’ {
    console.log’Fetch aborted’.
    } else {
    console.error’Fetch error:’, error.
    }.

    // To cancel the request after a delay e.g., 5 seconds
    setTimeout => controller.abort, 5000.

    This is extremely useful for managing network requests, especially in components that might unmount before a fetch completes, preventing memory leaks or unnecessary state updates.

Table of Contents

Understanding the Core Principles of Fetch API

The Fetch API is a modern, promise-based mechanism for making network requests in the browser and in Node.js environments.

It’s a fundamental part of contemporary web development, offering a more flexible and powerful alternative to older methods like XMLHttpRequest. At its heart, Fetch aims to provide a generic definition of Request and Response objects, and a way to interact with them programmatically.

This approach ensures consistency and simplifies handling various types of network interactions, from simple data retrieval to complex file uploads.

Its promise-based nature aligns perfectly with asynchronous JavaScript patterns, making code cleaner and more readable, especially when dealing with sequences of network operations or concurrent requests.

Basic GET Requests with Fetch

The simplest and most common use case for the Fetch API is performing a GET request to retrieve data from a server.

This involves calling the fetch global method with a single argument: the URL of the resource you want to access.

The method returns a Promise that resolves to a Response object.

This Response object contains information about the response, such as its status code, headers, and the response body.

The initial Promise returned by fetch only rejects if a network error occurs e.g., DNS lookup failure, no internet connection. It does not reject for HTTP errors like 404 Not Found or 500 Internal Server Error. For these, you must explicitly check the response.ok property or response.status property within the first .then block.



fetch'https://jsonplaceholder.typicode.com/posts/1'
  .thenresponse => {


   // Check if the response was successful status code 200-299
    if !response.ok {


     // If not successful, throw an error to be caught by the .catch block


     throw new Error`HTTP error! Status: ${response.status}`.
    }
    // Parse the JSON body of the response
    // response.json also returns a Promise
    return response.json.
  }
  .thendata => {
    // Process the parsed JSON data
    console.log'Fetched data:', data.
    console.log`Title: ${data.title}`.
    console.log`Body: ${data.body}`.
  .catcherror => {


   // Handle any errors that occurred during the fetch operation or parsing


   console.error'There was a problem with the fetch operation:', error.
  }.

Key takeaways for GET requests:

  • Simplicity: A single URL is often all you need.
  • Promise-based: fetch returns a Promise. This promise resolves to a Response object.
  • Two-step parsing: You need to call methods like response.json, response.text, or response.blob to extract the actual data from the response body. These methods also return promises.
  • Error handling nuance: fetch does not reject on HTTP error codes like 404, 500. You must manually check response.ok or response.status.

This basic GET request structure forms the backbone of fetching any kind of public data, be it news articles from an API, product listings for an e-commerce site, or user profiles for a social application.

According to Statista, the number of API calls made globally is expected to grow significantly, highlighting the increasing reliance on web APIs and, by extension, the Fetch API, for data exchange.

In 2022, API calls were already in the trillions, and this trend is only accelerating.

Advanced Fetch Options: POST, PUT, DELETE, and Custom Headers

While GET requests are for retrieving data, many applications require sending data to the server e.g., creating a new user, updating a record, deleting an item. The Fetch API handles these “mutation” operations by allowing you to pass a second argument to the fetch function: an init object.

This init object is where you define the HTTP method, custom headers, and the request body.

Specifying the HTTP Method method property

The method property within the init object allows you to specify the HTTP verb for your request. Common methods include:

  • 'POST': Used to submit data to be processed to a specified resource. Often results in a new resource being created.
  • 'PUT': Used to update a resource or create it if it doesn’t exist.
  • 'DELETE': Used to delete a specified resource.
  • 'PATCH': Used to apply partial modifications to a resource.

// Example: POST request to create a new post
const newPost = {
title: ‘My First Fetch Post’,

body: ‘This is the body of my first post created using Fetch API.’,
userId: 1,
}.

Fetch’https://jsonplaceholder.typicode.com/posts‘, {
method: ‘POST’, // Specify the HTTP method
headers: {

'Content-Type': 'application/json', // Inform the server that we are sending JSON


'Accept': 'application/json'        // Inform the server that we prefer JSON in response

},

body: JSON.stringifynewPost // Convert JavaScript object to JSON string for the body
}
.thenresponse => {
if !response.ok {

throw new Error`HTTP error! Status: ${response.status}`.

}
return response.json.
.thendata => {
console.log’New post created:’, data.

// Expected response might include the new ID assigned by the server
console.logNew post ID: ${data.id}.
.catcherror => {
console.error’Error creating post:’, error.
}.

Setting Custom Headers headers property

The headers property is an object where each key-value pair represents an HTTP header. This is crucial for:

  • Content-Type: Tells the server what type of data you’re sending in the request body e.g., application/json, application/x-www-form-urlencoded. This is vital for the server to correctly parse your request.
  • Accept: Tells the server what content types you prefer to receive in the response.
  • Authorization: Used for sending authentication tokens e.g., Bearer YOUR_TOKEN to access protected resources.
  • Custom Headers: Any other specific headers required by your API e.g., API keys, client identifiers.

// Example: PUT request with an authorization header
const updatedPost = {

id: 1, // Often included for PUT requests to specify which resource to update
title: ‘Updated Fetch Post Title’,

body: ‘This post has been updated using Fetch API.’,

Const authToken = ‘your_super_secret_token_here’. // In a real app, this would come from authentication

Fetch’https://jsonplaceholder.typicode.com/posts/1‘, {
method: ‘PUT’,
‘Content-Type’: ‘application/json’,

'Authorization': `Bearer ${authToken}` // Sending an authentication token

body: JSON.stringifyupdatedPost

console.log’Post updated successfully:’, data.
console.error’Error updating post:’, error.

Including the Request Body body property

For POST, PUT, and PATCH requests, you’ll typically include a body property. The value of body can be:

  • A String: Most commonly, a JSON string JSON.stringifyyourObject.
  • A FormData object: For sending form data, especially with file uploads.
  • A URLSearchParams object: For URL-encoded form data.
  • A Blob or BufferSource: For binary data.

Important Note on body and Content-Type:
When sending JSON, you must set Content-Type: 'application/json'.
When sending FormData, you must not set the Content-Type header yourself. Fetch will automatically set it correctly, including the necessary boundary string for multipart requests.

These advanced options make Fetch API incredibly versatile for interacting with any RESTful API, enabling full CRUD Create, Read, Update, Delete operations from your client-side JavaScript.

The proper use of headers, especially Content-Type and Authorization, is critical for ensuring that your requests are correctly interpreted by the server and that security protocols are adhered to.

Industry reports indicate that over 80% of web applications rely on APIs for data exchange, making mastery of these Fetch options indispensable.

Error Handling and Status Codes in Fetch API

Effective error handling is paramount for building robust and user-friendly applications. While Fetch API simplifies network requests, understanding how it handles errors, particularly HTTP status codes, is crucial. Unlike XMLHttpRequest, fetch‘s promise will only reject if a network error occurs e.g., a complete inability to connect, DNS resolution failure, network timeout. It will not reject for HTTP errors like 404 Not Found, 401 Unauthorized, 500 Internal Server Error, etc. These are considered successful responses from the server’s perspective, even if they indicate a problem with the request itself.

To handle HTTP errors, you must explicitly check the Response object’s properties within the first .then block.

Key Response Properties for Error Handling:

  1. response.ok: A boolean that is true if the HTTP status code is in the range 200-299 inclusive, indicating a successful response. Otherwise, it’s false. This is the most common and convenient way to check for successful HTTP responses.
  2. response.status: An integer representing the HTTP status code e.g., 200, 404, 500.
  3. response.statusText: A string representing the status message e.g., “OK”, “Not Found”, “Internal Server Error”.

Structured Error Handling Example:

Fetch’https://jsonplaceholder.typicode.com/posts/99999‘ // This ID likely doesn’t exist, will result in a 404

  // If response.ok is false, it means an HTTP error 4xx or 5xx occurred.


  // We throw an Error to propagate it to the .catch block.


  // We can include more context in the error message for better debugging.


  const error = new Error`HTTP error! Status: ${response.status} - ${response.statusText}`.


  // Optionally, if the server sends error details in the JSON body,


  // you can parse it and attach to the error object.


  // For example: if response.status === 401 { return response.json.thendata => { error.details = data. throw error. }. }
   throw error.

// Reaching here means an HTTP error, so we reject the promise chain.

// If response.ok is true, parse the JSON body.


console.log'Data fetched successfully:', data.
 // This catch block handles:


// 1. Network errors e.g., server unreachable, CORS issues.


// 2. Errors thrown manually from the .then block our HTTP errors.


// 3. Errors during JSON parsing e.g., malformed JSON.


console.error'Fetch operation failed:', error.message.


if error.status { // Custom property if we added it for HTTP errors


  console.error`Status code: ${error.status}`.
 // Implement user feedback logic here
 // e.g., display an alert, update UI state

Common Error Scenarios and Best Practices:

  1. 4xx Client Errors e.g., 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found:

    These indicate that the client has made a mistake.

Your error handling should usually inform the user about what went wrong and how they can fix it e.g., “Please check your input,” “You are not authorized to view this content”.
2. 5xx Server Errors e.g., 500 Internal Server Error, 503 Service Unavailable:
These indicate a problem on the server’s side.

Your application should ideally show a generic “Something went wrong on our end, please try again later” message and possibly log the error for developers.
3. Network Errors:

These are the true rejections of the `fetch` promise.

They mean no response was received from the server at all.

Users should be informed about their internet connection or that the service is temporarily unavailable.
4. Parsing Errors:

If `response.json` fails because the response body isn't valid JSON, it will also reject the promise.

Ensure your server consistently returns valid JSON for application/json responses.

According to a study by Akamai, robust error handling can significantly improve user retention, with slow or broken pages leading to high bounce rates.

Specifically, pages taking more than 3 seconds to load see a 53% abandonment rate on mobile.

While not directly related to fetch error handling, this data underscores the importance of a smooth user experience, which includes clear and actionable error messages when network requests fail.

Handling Request and Response Body Types

The Fetch API is designed to work with various data formats for both sending requests and receiving responses.

Understanding these body types is essential for interacting with diverse APIs and handling files.

Request Body body property in init object

When performing POST, PUT, or PATCH requests, you’ll need to send data in the request body. The body property can accept several types:

  1. String most commonly JSON:
    This is the most frequent use case for sending structured data. You typically convert a JavaScript object into a JSON string using JSON.stringify. You must set the Content-Type header to application/json.

    Const data = { username: ‘testuser’, email: ‘[email protected]‘ }.
    fetch’/api/users’, {

    headers: { ‘Content-Type’: ‘application/json’ },
    body: JSON.stringifydata
    }.

  2. FormData:
    Ideal for sending form data, especially when dealing with file uploads or when the data mimics an HTML form submission multipart/form-data. You create a FormData object and append key-value pairs. Crucially, do NOT set the Content-Type header yourself when using FormData. Fetch will automatically set it to multipart/form-data with the correct boundary.
    formData.append’age’, 30.
    const fileInput = document.querySelector’#profilePicture’. // Assuming an
    if fileInput.files {

    formData.append’profile’, fileInput.files. // Append the File object

    fetch’/api/upload’, {

    body: formData // Fetch sets Content-Type automatically

  3. URLSearchParams:

    Used for sending data in application/x-www-form-urlencoded format, typically for form submissions where no files are involved.

This is less common with Fetch, as JSON is often preferred, but useful for older APIs.
const params = new URLSearchParams.
params.append’name’, ‘Bob’.
params.append’city’, ‘New York’.

 fetch'/api/search', {


  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
   body: params
  1. Blob or BufferSource e.g., ArrayBuffer, TypedArray:

    For sending raw binary data, such as images or other file types directly.

You would set the Content-Type to the appropriate MIME type e.g., image/png, application/octet-stream.

Response Body Response object methods

Once you receive a Response object, you need to extract the actual data from its body.

The Response object provides several methods, each returning a promise that resolves with the parsed content:

  1. response.json:
    Parses the response body as JSON.

This is by far the most common method for API responses.

The promise resolves with the parsed JavaScript object or array.
fetch’/api/data’

  .thendata => console.logdata. // { key: "value", ... }
  1. response.text:

    Reads the response body and returns it as a plain text string.

Useful for APIs that return non-JSON text, HTML fragments, or XML.
fetch’/api/html-snippet’
.thenresponse => response.text

  .thenhtml => console.loghtml. // "<div>Some HTML content</div>"
  1. response.blob:

    Reads the response body and returns it as a Blob object.

A Blob Binary Large Object represents raw, immutable data.

This is essential for handling binary files like images, audio, or PDFs received from the server.
fetch’/api/image.png’
.thenresponse => response.blob
.thenblob => {

    // Create a URL for the blob and display it


    const imageUrl = URL.createObjectURLblob.


    const imgElement = document.createElement'img'.
     imgElement.src = imageUrl.
     document.body.appendChildimgElement.


    // Don't forget to revoke the URL when no longer needed to free up memory
     // URL.revokeObjectURLimageUrl.
  1. response.arrayBuffer:

    Reads the response body and returns it as an ArrayBuffer. This is a generic, fixed-length binary data buffer.

Useful for low-level processing of binary data, perhaps for WebAssembly or specific cryptographic operations.
fetch’/api/binary-data’
.thenresponse => response.arrayBuffer
.thenbuffer => {

    // Process the ArrayBuffer, e.g., convert to a TypedArray
     const uint8Array = new Uint8Arraybuffer.
     console.log'Binary data:', uint8Array.
  1. response.formData:

    Parses the response body as FormData. Less common for responses, but can be useful if a server explicitly returns data in multipart/form-data format.

Choosing the correct request and response body types is critical for successful communication with different backend services.

Misconfigurations in Content-Type headers or incorrect parsing methods are common sources of API integration issues.

Developers often spend considerable time debugging these types of mismatches.

According to a recent survey by Postman, 89% of developers use APIs daily, emphasizing the need for a deep understanding of these interaction patterns.

Aborting Fetch Requests with AbortController

In modern web applications, managing the lifecycle of network requests is just as important as making them.

Imagine a user rapidly typing into a search box: each keystroke might trigger a new search request.

If previous requests are not cancelled, they might complete out of order, leading to stale or incorrect data being displayed.

Similarly, when a user navigates away from a component, any pending network requests initiated by that component should ideally be cancelled to prevent memory leaks or attempts to update non-existent UI elements.

The Fetch API itself does not have a built-in cancellation mechanism directly on the Promise object.

Instead, it leverages the AbortController interface, a generic way to signal an abort operation.

How AbortController Works:

  1. Create an AbortController instance:

  2. Get the signal property:

    The controller.signal is an AbortSignal object.

This signal can be passed to the fetch function’s init object.
fetchurl, { signal: signal }
// … rest of your fetch chain
3. Abort the request:

When you want to cancel the request, call `controller.abort`. This will trigger the `abort` event on the `signal` object, causing any `fetch` requests associated with that signal to be aborted.
 controller.abort.

Example of Aborting a Fetch Request:

let currentFetchController. // To keep track of the ongoing fetch

function fetchSearchResultsquery {

// If there’s an ongoing fetch, abort it before starting a new one
if currentFetchController {
currentFetchController.abort.

console.log'Previous fetch request aborted.'.

currentFetchController = new AbortController.
const signal = currentFetchController.signal.

fetchhttps://api.example.com/search?q=${query}, { signal }
.thenresponse => {
if !response.ok {

    throw new Error`HTTP error! Status: ${response.status}`.
   }
   return response.json.
 .thendata => {
   console.log'Search results:', data.
   // Update UI with new data


  // Ensure the controller is cleared once fetch completes successfully
   currentFetchController = null.
 .catcherror => {
   if error.name === 'AbortError' {


    console.warn'Fetch request was aborted.'.
   } else {
     console.error'Fetch error:', error.


  // Ensure the controller is cleared even on error

}

// Simulate user typing:
fetchSearchResults’apple’.

SetTimeout => fetchSearchResults’banana’, 500. // This will abort the ‘apple’ fetch

SetTimeout => fetchSearchResults’orange’, 1000. // This will abort the ‘banana’ fetch

Important Considerations:

  • AbortError: When a fetch request is aborted, the promise returned by fetch rejects with a DOMException whose name property is 'AbortError'. It’s crucial to distinguish this from other network errors in your catch block to handle it gracefully e.g., avoid showing an “error” message to the user for an intentional abort.
  • Resource Management: Aborting a fetch request sends a signal to the browser that you’re no longer interested in the response. The browser attempts to stop the network activity, but it’s not guaranteed that the underlying HTTP request will be immediately terminated on the server side. It mainly helps prevent the client-side JavaScript from processing a potentially stale response.
  • Cleanup: In single-page applications SPAs or components with specific lifecycles like React useEffect or Vue onUnmounted, you should always abort pending requests when a component unmounts to prevent memory leaks and “can’t perform an update on an unmounted component” warnings.

By effectively implementing AbortController, developers can build more responsive, efficient, and resilient web applications that gracefully manage the flow of network data, enhancing the user experience.

Performance data from Google Lighthouse often highlights the negative impact of unmanaged network requests on page load times and responsiveness, underscoring the value of proper abortion techniques.

Working with Concurrent and Sequential Fetch Requests

In many real-world applications, you’ll encounter scenarios where you need to make multiple API calls.

These calls might need to run simultaneously concurrently to speed up data loading, or in a specific order sequentially where one request depends on the result of a previous one.

JavaScript’s Promise API, combined with Fetch, provides powerful tools for managing these patterns.

Concurrent Requests Promise.all, Promise.allSettled, Promise.race

When you need to fetch multiple resources independently and only proceed once all or some of them have completed, Promise combinators are invaluable.

  1. Promise.alliterable:
    This method takes an iterable like an array of promises and returns a single Promise. This returned Promise resolves with an array of the results of the input promises, in the same order as the input. It rejects if any of the input promises reject. This is ideal when you need all requests to succeed for your application to function correctly.

    Const fetchUsers = fetch’https://jsonplaceholder.typicode.com/users’.thenres => res.json.

    Const fetchPosts = fetch’https://jsonplaceholder.typicode.com/posts’.thenres => res.json.

    Const fetchAlbums = fetch’https://jsonplaceholder.typicode.com/albums’.thenres => res.json.

    Promise.all
    .then => {

    console.log'All data fetched successfully:'.
     console.log'Users count:', users.length.
     console.log'Posts count:', posts.length.
    
    
    console.log'Albums count:', albums.length.
    
    
    // You can now work with all three datasets
    
    
    console.error'One of the fetches failed:', error.
    
    
    // Handle the error that occurred in any of the promises
    

    Use case: Loading dashboard widgets where all data points e.g., user stats, latest orders, recent activity are needed before displaying the dashboard.

  2. Promise.allSettlediterable ES2020+:

    Similar to Promise.all, but it waits for all promises to either fulfill or reject.

It always resolves, returning an array of objects, each describing the outcome of a promise either { status: 'fulfilled', value: result } or { status: 'rejected', reason: error }. This is useful when you want to execute multiple independent requests and know the outcome of each, regardless of success or failure.

const fetchValid = fetch'https://jsonplaceholder.typicode.com/users/1'.thenres => res.json.


const fetchInvalid = fetch'https://jsonplaceholder.typicode.com/nonexistent'.thenres => {


    if !res.ok throw new Error`HTTP error! Status: ${res.status}`.
     return res.json.

 Promise.allSettled
   .thenresults => {
     console.log'All requests settled:'.
     results.forEachresult, index => {
       if result.status === 'fulfilled' {


        console.log`Request ${index + 1} succeeded:`, result.value.
       } else {


        console.error`Request ${index + 1} failed:`, result.reason.
       }
     }.
Use case: Sending multiple log events, tracking independent statistics, or fetching optional data components where some failures won't break the entire application.
  1. Promise.raceiterable:

    Returns a promise that fulfills or rejects as soon as one of the promises in the iterable fulfills or rejects, with the value or reason from that promise.

This is useful for competitive requests or implementing timeouts.

const fetchFast = fetch'https://jsonplaceholder.typicode.com/posts/1'.thenres => res.json.


const fetchSlow = new Promiseresolve => setTimeout => resolve'Slow data', 3000.

 Promise.race
   .thenresult => {


    console.log'The first promise to settle wins:', result. // Will log data from fetchFast


    console.error'The first promise to reject wins:', error.

 // Example with timeout:
 const timeout = new Promise_, reject =>


  setTimeout => rejectnew Error'Request timed out', 5000
 .


Promise.race


  .thendata => console.log'Data received before timeout:', data


  .catcherror => console.error'Request failed or timed out:', error.message.
Use case: Implementing a timeout for a network request, or fetching data from multiple redundant servers and using the first one that responds.

Sequential Requests async/await

When one request’s data is needed to make a subsequent request, you need sequential execution.

While you can chain .then calls, async/await provides a much cleaner, synchronous-looking syntax for sequential asynchronous operations.

async function getUserAndPostsuserId {
try {
// 1. Fetch user details

const userResponse = await fetch`https://jsonplaceholder.typicode.com/users/${userId}`.
 if !userResponse.ok {


  throw new Error`Failed to fetch user: ${userResponse.status}`.
 const user = await userResponse.json.
 console.log'User fetched:', user.name.

 // 2. Use user ID to fetch their posts


const postsResponse = await fetch`https://jsonplaceholder.typicode.com/posts?userId=${userId}`.
 if !postsResponse.ok {


  throw new Error`Failed to fetch posts: ${postsResponse.status}`.
 const posts = await postsResponse.json.


console.log`Posts by ${user.name}:`, posts.length.

 return { user, posts }.

} catch error {

console.error'Error in sequential fetch:', error.message.
 throw error. // Re-throw to allow further handling

getUserAndPosts1

console.log'Complete user and posts data:', data.

.catcherr => {
console.error’Operation failed:’, err.
Use case: Fetching a user ID, then using that ID to fetch the user’s profile details or a list of their specific items.

According to a study by Statista, the global market for APIs is projected to grow substantially, reaching over $13 billion by 2027. This growth implies an increasing complexity in API interactions, making efficient management of concurrent and sequential requests with Fetch and Promises more critical than ever for building high-performance applications.

Cross-Origin Resource Sharing CORS with Fetch API

Cross-Origin Resource Sharing CORS is a security mechanism implemented by web browsers to restrict how resources on one domain the “origin” can be requested by a web page from another domain.

Without CORS, web pages could make arbitrary requests to any domain, potentially leading to security vulnerabilities like cross-site request forgery CSRF or data leaks.

When you use the Fetch API to make a request to a different origin, the browser enforces these CORS policies.

Understanding Same-Origin Policy:

By default, browsers enforce the Same-Origin Policy, which means a web page can only request resources from the exact same origin scheme, host, and port from which it was loaded.

  • Same Origin: https://example.com/page.html can request https://example.com/api/data.
  • Different Origin CORS applies: https://app.example.com wants to request https://api.thirdparty.com/data. This is a cross-origin request.

How CORS Works with Fetch:

When a cross-origin request is made via Fetch:

  1. Simple Requests:
    Certain requests are considered “simple” e.g., GET, POST, HEAD with specific Content-Type headers like application/x-www-form-urlencoded, multipart/form-data, or text/plain. For these, the browser sends the request directly with an Origin header. The server then needs to respond with an Access-Control-Allow-Origin header that includes the client’s origin or * wildcard. If the server doesn’t send the appropriate header, or if the client’s origin isn’t allowed, the browser will block the response, and your Fetch promise will reject with a network error often visible as a CORS error in the browser’s developer console.

  2. Preflighted Requests:

    More complex requests e.g., PUT, DELETE, requests with custom headers, or Content-Type: application/json trigger a “preflight” OPTIONS request.

Before sending the actual request, the browser first sends an OPTIONS request to the server to ask for permission.

This preflight request includes Access-Control-Request-Method and Access-Control-Request-Headers headers.

The server must respond to this OPTIONS request with appropriate Access-Control-Allow-Origin, Access-Control-Allow-Methods, and Access-Control-Allow-Headers headers.

If the preflight succeeds, the browser then sends the actual request.

If it fails, the actual request is never sent, and a CORS error is thrown.

Common CORS Errors and Solutions:

You’ll often see errors like “No ‘Access-Control-Allow-Origin’ header is present on the requested resource” in your browser console when CORS issues arise.

Solutions for CORS issues:

  1. Server-Side Configuration Most Common and Correct:

    The most robust solution is to configure the server API to send the necessary CORS headers. This is typically done by the backend developer.

    • Allow Specific Origins: Access-Control-Allow-Origin: https://yourfrontend.com
    • Allow Multiple Specific Origins: requires server logic to echo the client’s origin if it’s in an allowed list
    • Allow All Origins Less Secure, for public APIs/development: Access-Control-Allow-Origin: *
    • Allow Specific Methods: Access-Control-Allow-Methods: GET, POST, PUT, DELETE
    • Allow Specific Headers: Access-Control-Allow-Headers: Content-Type, Authorization
    • Include Credentials for cookies/HTTP auth: Access-Control-Allow-Credentials: true requires Access-Control-Allow-Origin to be a specific origin, not *.
  2. Using mode in Fetch init object:

    The mode property in the fetch init object controls how CORS is handled.

    • 'cors' default: Standard CORS behavior.
    • 'no-cors': Allows requests to be made to other origins, but the browser will not expose the response to JavaScript. You cannot read the response body, status, or headers. This is mainly for sending analytics data where you don’t care about the response.
    • 'same-origin': Only allows requests to the same origin. Cross-origin requests will cause a network error.
    • 'navigate': For navigating between pages not typically used for Fetch API calls.

    fetch’https://api.thirdparty.com/data‘, {
    mode: ‘cors’ // Default, explicitly stating
    .thenresponse => response.json

    .catcherror => console.error’CORS or Network error:’, error.

    // Example of ‘no-cors’ response inaccessible

    Fetch’https://api.thirdparty.com/analytics‘, {

    mode: ‘no-cors’, // Request is sent, but response properties are opaque

    body: JSON.stringify{ event: ‘page_view’ }

    // response.type will be ‘opaque’, response.status will be 0, etc.

    console.log’Opaque response cannot read content:’, response.

    .catcherror => console.error’Error:’, error.

  3. Proxy Server for Development/Legacy APIs:

    For development, or when you cannot control the backend, you can set up a proxy server e.g., using Node.js, Nginx, or a webpack-dev-server proxy. Your frontend makes requests to your own proxy, which then forwards them to the actual API and returns the response.

Since the proxy is on the same origin as your frontend, the browser’s CORS policy is satisfied.

CORS is a vital security feature.

While it can sometimes be frustrating during development, it protects users from malicious cross-origin attacks.

Proper understanding and configuration, primarily on the server-side, are essential for successful cross-origin API interactions.

According to OWASP Open Web Application Security Project, improper CORS configuration is a common security vulnerability, underscoring the need for careful implementation.

Security Best Practices with Fetch API

While the Fetch API itself is a low-level network primitive and doesn’t inherently introduce security vulnerabilities, how you use it to interact with backend services can significantly impact your application’s security posture.

Implementing robust security practices is paramount to protect user data, prevent unauthorized access, and maintain the integrity of your application.

1. Always Use HTTPS

This is the most fundamental security practice.

HTTPS HTTP Secure encrypts communication between the client and the server, protecting data from eavesdropping, tampering, and message forgery.

If your application sends or receives any sensitive data passwords, personal information, authentication tokens, using HTTP is a critical vulnerability.

  • Impact: Prevents Man-in-the-Middle MitM attacks where attackers could intercept, read, or modify data in transit.
  • Fetch Implication: Ensure all URLs you fetch from start with https://.

2. Implement Proper Authentication and Authorization

When fetching or sending data to protected resources, authentication and authorization are key.

  • Authentication: Verifying the identity of the user. Common methods with Fetch include:
    • Bearer Tokens OAuth 2.0, JWT: The most common and recommended approach for APIs. After a user logs in, the server issues a token. This token is then sent with subsequent Fetch requests in the Authorization header:
      fetch'/api/protected-data', {
        headers: {
      
      
         'Authorization': `Bearer ${localStorage.getItem'authToken'}`
      
    • API Keys: Less secure than tokens for user authentication, but common for identifying client applications e.g., for rate limiting or public API access.
      fetch’/api/public-data’, {
      ‘X-API-Key’: ‘YOUR_API_KEY_HERE’
  • Authorization: Determining what an authenticated user is allowed to do. This is primarily a server-side concern, but your frontend should adapt its UI based on the user’s permissions, which are often communicated via API responses.
  • Avoid Storing Sensitive Data in LocalStorage: While common for JWTs, localStorage is vulnerable to XSS attacks. If an attacker injects malicious JavaScript, they can easily steal tokens from localStorage. More secure alternatives include:
    • HttpOnly Cookies: Cookies marked HttpOnly cannot be accessed by JavaScript, making them immune to typical XSS token theft. However, they are vulnerable to CSRF.
    • Memory in-memory state: For very short-lived tokens, but tokens are lost on refresh.
    • Web Workers for complex setups: Can provide a more isolated environment for token handling.

3. Handle and Validate All Input Data

Any data sent from the client to the server, whether in the URL, headers, or body, must be treated as untrusted.

  • Client-Side Validation: While useful for user experience immediate feedback, client-side validation can always be bypassed. It’s not a security measure.
  • Server-Side Validation: Crucial. The server must thoroughly validate and sanitize all incoming data before processing it or storing it in a database. This prevents SQL injection, XSS, and other injection attacks.
  • Fetch Implication: When building request bodies JSON.stringify, FormData, ensure the data originates from trusted sources or is properly encoded if user-supplied.

4. Protect Against Cross-Site Request Forgery CSRF

CSRF attacks trick authenticated users into submitting malicious requests without their knowledge.

  • Server-Side CSRF Tokens: The most common and effective defense. The server sends a unique, unpredictable token with the initial page load. Subsequent POST/PUT/DELETE requests must include this token e.g., in a custom header like X-CSRF-Token. The server validates the token.
    • Fetch Implication: Your Fetch requests would include this token:
      fetch’/api/transfer-funds’, {
      method: ‘POST’,
      ‘Content-Type’: ‘application/json’,

      ‘X-CSRF-Token’: document.querySelector’meta’.content // Get token from meta tag
      },
      body: JSON.stringify{ amount: 100 }

  • SameSite Cookies: The SameSite attribute on cookies can help mitigate CSRF by controlling when cookies are sent with cross-site requests. Options include Strict, Lax default for many browsers, and None.

5. Securely Manage CORS Cross-Origin Resource Sharing

As discussed, CORS allows controlled cross-origin requests. Misconfigurations can lead to security holes.

  • Server-Side Access-Control-Allow-Origin: Configure your backend to only allow specific, trusted origins, never using * in production unless the API is truly public and doesn’t expose sensitive data.
  • Access-Control-Allow-Credentials: true: Only use this if truly necessary and combine it with specific Access-Control-Allow-Origin values.

6. Error Logging and Monitoring

Implement robust logging for server-side errors, especially those related to authentication, authorization, or data validation. Monitor these logs for suspicious activity.

7. Keep Dependencies Updated

Regularly update your frontend and backend libraries to patch known security vulnerabilities.

By adopting these security best practices in conjunction with the Fetch API, developers can significantly reduce the attack surface of their web applications, building more secure and trustworthy systems for their users.

Cybersecurity breaches are becoming increasingly common.

IBM’s 2023 Cost of a Data Breach Report states the average cost of a data breach globally was $4.45 million, highlighting the critical importance of proactive security measures.

Frequently Asked Questions

What is the Fetch API in JavaScript?

The Fetch API is a modern, promise-based interface in JavaScript for making network requests.

It provides a more powerful and flexible way to interact with resources across the network compared to older methods like XMLHttpRequest. It is primarily used for fetching data from web APIs, submitting forms, or handling file uploads.

How do I make a basic GET request using Fetch?

To make a basic GET request, you call fetch with the URL of the resource you want to retrieve.

It returns a Promise that resolves to a Response object.

You then typically use response.json or response.text, response.blob to parse the response body.
fetch’https://api.example.com/data
.thenresponse => response.json
.thendata => console.logdata
.catcherror => console.error’Error:’, error. How to scrape glassdoor

How do I send data with Fetch using POST?

To send data with a POST request, you provide a second argument to fetch: an options object.

This object specifies the method: 'POST', headers e.g., Content-Type: 'application/json', and body e.g., JSON.stringifyyourDataObject.
fetch’https://api.example.com/submit‘, {
method: ‘POST’,
headers: { ‘Content-Type’: ‘application/json’ },
body: JSON.stringify{ key: ‘value’ }

How do I handle HTTP errors like 404 or 500 with Fetch?

The fetch promise only rejects on network errors.

For HTTP errors status codes outside 200-299, you must explicitly check response.ok or response.status within the first .then block.

If response.ok is false, you should throw new Error to propagate the error to the .catch block.
fetch’https://api.example.com/nonexistentDataset vs database

.catcherror => console.error’Fetch error:’, error.

What is the difference between response.json and response.text?

response.json parses the response body as a JSON string and returns a Promise that resolves with the resulting JavaScript object or array.

response.text reads the response body and returns a Promise that resolves with the body as a plain text string.

Choose json for structured data from APIs and text for HTML, XML, or plain text.

Can I send files with Fetch API?

Yes, you can send files using the FormData object in the Fetch API. Append your file from an <input type="file"> element to a FormData instance and set it as the body of your fetch request. Do not set the Content-Type header manually. Fetch will handle it automatically for FormData.
const formData = new FormData. Requests vs httpx vs aiohttp

FormData.append’profileImage’, fileInput.files.

Fetch’/upload’, { method: ‘POST’, body: formData }.

How do I add custom headers to a Fetch request?

You can add custom headers by including a headers property in the init object passed to fetch. This property should be an object where keys are header names and values are header values.
fetch’/api/data’, {
‘Authorization’: ‘Bearer your_token_here’,
‘X-Custom-Header’: ‘MyValue’

How do I cancel a Fetch request?

You can cancel a Fetch request using the AbortController API.

Create an AbortController instance, get its signal property, and pass this signal to the fetch function’s init object. Few shot learning

Call controller.abort when you want to cancel the request.
const controller = new AbortController.

Fetch’/api/long-task’, { signal: controller.signal }

.catcherror => { if error.name === ‘AbortError’ console.log’Aborted!’. }.
controller.abort.

What is Promise.all and how is it used with Fetch?

Promise.all takes an array of Promises e.g., multiple fetch calls and returns a single Promise.

This returned Promise resolves when all of the input Promises have resolved, with an array of their results in the same order. It rejects if any of the input Promises reject. Best data collection services

It’s used for making concurrent requests where all results are needed.

Promise.all
.then => { /* process both */ }.

What is async/await and how does it relate to Fetch?

async/await is modern JavaScript syntax that allows you to write asynchronous code that looks and behaves more like synchronous code. It’s built on Promises.

You can await a fetch call and its subsequent response.json parsing, making sequential requests much cleaner to read and write.
async function getData {
const response = await fetch’/api/data’.

if !response.ok throw new Error'Network response was not ok.'.
 const data = await response.json.
 console.logdata.
 console.error'Fetch failed:', error.

What is CORS and how does it affect Fetch API?

CORS Cross-Origin Resource Sharing is a browser security mechanism that restricts web pages from making requests to a different domain than the one they originated from. Web scraping with perplexity

If your Fetch request is to a different origin, the server must explicitly allow it via CORS headers e.g., Access-Control-Allow-Origin. Otherwise, the browser will block the response, resulting in a CORS error.

Can Fetch API handle cookies?

Yes, Fetch API can handle cookies. By default, fetch requests will send cookies associated with the current domain. For cross-origin requests, you need to set credentials: 'include' in the init object, and the server must also respond with Access-Control-Allow-Credentials: true and a specific Access-Control-Allow-Origin not *.

Fetch’/api/auth-data’, { credentials: ‘include’ }.

What is response.blob used for in Fetch?

response.blob is used to read the response body as a Blob object, which represents raw, immutable data.

This is essential for fetching binary files like images, audio, or PDFs from a server and then processing them e.g., displaying an image, downloading a file.
fetch’/image.png’
.thenresponse => response.blob
.thenblob => {
const imageUrl = URL.createObjectURLblob. Web scraping with parsel

document.getElementById'myImage'.src = imageUrl.

How can I implement a timeout for a Fetch request?

You can implement a timeout using Promise.race with an AbortController. You create one Promise for your fetch call and another Promise that rejects after a specified time.

Whichever Promise settles first either the fetch resolves/rejects, or the timeout rejects determines the outcome of the Promise.race.

Const timeoutId = setTimeout => controller.abort, 5000. // 5 seconds timeout

Fetch’/api/slow-data’, { signal: controller.signal }

clearTimeouttimeoutId. // Clear timeout if fetch completes


clearTimeouttimeoutId. // Ensure timeout is cleared on any error
 if error.name === 'AbortError' {
   console.error'Request timed out!'.
 } else {
   console.error'Fetch error:', error.

What is the purpose of Headers object in Fetch?

While you can use a plain object for headers, the Headers interface provides a programmatic way to create and manipulate HTTP request/response headers. Web scraping with r

It’s useful when you need to dynamically add, get, or delete headers.
const myHeaders = new Headers.

MyHeaders.append’Content-Type’, ‘application/json’.

MyHeaders.append’Authorization’, ‘Bearer token123′.
fetch’/api/data’, { headers: myHeaders }.

Is Fetch API supported in all browsers?

Yes, Fetch API is widely supported in all modern browsers Chrome, Firefox, Safari, Edge. For older browsers like Internet Explorer, you would need to use a polyfill e.g., whatwg-fetch. Node.js also has native Fetch support since version 18.

How do I send URL-encoded form data with Fetch?

You can send URL-encoded form data using the URLSearchParams object. What is a dataset

Set the Content-Type header to application/x-www-form-urlencoded and stringify your URLSearchParams object for the body.
const params = new URLSearchParams.
params.append’name’, ‘John Doe’.
params.append’age’, ’30’.
fetch’/api/form’, {

headers: { ‘Content-Type’: ‘application/x-www-form-urlencoded’ },
body: params.toString

What are opaque responses in Fetch?

Opaque responses occur when you make a cross-origin request using mode: 'no-cors'. The browser sends the request, but it intentionally hides details of the response from JavaScript for security reasons.

This means you cannot access the response’s status code, headers, or body.

It’s typically used for sending simple data like analytics pings where you don’t need to read the server’s response. Best web scraping tools

How do I handle network connectivity issues with Fetch?

Network connectivity issues e.g., user is offline, server is down, DNS error will cause the fetch promise to reject.

You should catch these errors in your .catch block and provide appropriate feedback to the user, such as “No internet connection” or “Service unavailable.”

When should I use Fetch API over XMLHttpRequest XHR?

Fetch API is generally preferred over XMLHttpRequest for new development due to its modern, promise-based design.

Fetch provides a cleaner and more readable syntax, better error handling patterns with async/await, and a more consistent way to handle different request/response body types.

XHR can be more verbose and callback-hell prone for complex asynchronous operations. Backconnect proxies

Leave a Reply

Your email address will not be published. Required fields are marked *