Skip to content

How to Make HTTP Requests in Node.js With Fetch API: A Comprehensive Guide

The Fetch API provides a modern alternative to XMLHttpRequest for making network requests in JavaScript. With the release of Node.js v17.5, Fetch API is now available for use in server-side JavaScript as well.

In this in-depth guide, we‘ll cover how to fully leverage Fetch API for making HTTP requests in Node.js.

A Brief History of Fetch API

The Fetch API specification was first published in 2014 to provide a more powerful and flexible replacement for XMLHttpRequest (XHR). It uses Promises and other modern JavaScript features which enable cleaner asynchronous code compared to callbacks.

Browser support for Fetch API landed in Chrome and Firefox later in 2014. By 2017, Fetch had >90% browser support and was ready for mainstream use.

Meanwhile on the server side, node-fetch was released as a polyfill module in 2016. It allowed Node.js developers to use a Fetch API-like interface for HTTP requests.

Finally in Feb 2024, experimental support for Fetch API landed in Node.js v17.5. This eliminated the need for a separate polyfill module like node-fetch for server-side requests.

Fetch API adoption continues to grow thanks to its flexibility and ease of use for modern web development. Next, let‘s see it in action!

Making Requests with Fetch API

The syntax for making a request with Fetch API is simple:

fetch(url, options) 

This returns a Promise that resolves with a Response object containing the request result.

To make a basic GET request:

fetch(‘https://api.example.com/data‘)
  .then(response => {
    // handle response
  })

Fetch API defaults to GET requests if no method is specified.

For other HTTP methods, pass the method in options:

fetch(url, {
  method: ‘POST‘ 
})

Supported HTTP request methods in Fetch API are:

  • GET
  • POST
  • PUT
  • DELETE
  • HEAD
  • OPTIONS

Next let‘s look at sending request headers, data, and other options with Fetch.

Fetch API Options

The second optional argument for fetch() is an options object that allows you to customize the request:

const options = {
  method: ‘POST‘,
  headers: {
    ‘Content-Type‘: ‘application/json‘
  },
  body: JSON.stringify(data) 
}

fetch(url, options)

Here are some of the commonly used options:

OptionDescription
methodHTTP request method e.g. GET, POST
headersObject of request headers to set
bodyRequest body (string, FormData, BufferSource, etc)
redirectRedirect mode e.g. follow, error
cacheCache mode e.g. default, no-cache

This allows full control over the HTTP request from within Fetch.

Request Body

For POST, PATCH, and PUT requests, you will likely need to send data in the request body.

The body option accepts different types including:

  • String
  • FormData
  • JSON object – convert to string with JSON.stringify()
  • Blob / BufferSource
  • ArrayBuffer

For example, to send JSON:

const body = {
  name: ‘John‘,
  age: 30
};

fetch(url, {
  method: ‘POST‘,
  headers: {
    ‘Content-Type‘: ‘application/json‘
  },
  body: JSON.stringify(body)
})  

Form data can be sent as:

const formData = new FormData();
formData.append(‘name‘, ‘John‘);

fetch(url, {
  method: ‘POST‘,
  body: formData
});

This makes it straightforward to send requests with any kind of structured data.

Handling Fetch Responses

The Promise returned by the Fetch call resolves to a Response object containing the response details:

fetch(url).then(response => {

  // response.status
  // response.ok
  // response.headers

})

The Response object has properties like status, ok, and headers which contain metadata about the response.

To extract the actual response body content, these methods are available:

  • .text() – Get response as text string
  • .json() – Parse JSON response
  • .blob() – Get Blob object
  • .arrayBuffer() – Get ArrayBuffer

For example:

fetch(url)
  .then(response => response.json())
  .then(data => {
    // use JSON data
  })

So Fetch API handles parsing the response for you based on the Content-Type header.

Response Status Codes

HTTP response status codes like 200, 404, 500 are available on the Response status property.

To check if a request succeeded:

fetch(url).then(response => {
  if (response.ok) {
    // request succeeded
  } else {  
    // request failed
  }
})

The .ok property is a shorthand for status in the 200-range.

Streams

Fetch API also supports streams which allow processing the response incrementally as chunks arrive:

const response = fetch(url);

const reader = response.body.getReader(); 

reader.read().then( ({done, value}) => {
  // handle chunk
}) 

This avoids buffering the entire response in memory for large responses.

Handling Errors

Since Fetch uses Promises, errors can be caught using .catch():

fetch(url)
  .then(/* ... */)
  .catch(error => {
    console.error(error)  
  });

Or with async/await:

try {
  const response = await fetch(url);
} catch (error) {
  console.error(error);
}

This catches any network errors or HTTP status errors.

To retry on failure, use recursion or a retry loop:

const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms))

const fetchWithRetry = async (url, retries=3, delayMs=3000) => {

  for (let retry=1; retry<=retries; retry++) {

    try {
      return await fetch(url); 
    } catch(error) {

      if (retry < retries) {
        await delay(delayMs);
        continue;  
      }

      throw error;
    }
  }

}

More robust error handling ensures the request succeeds even with faults.

Comparison of Fetch API, Axios, and Node HTTP Module

Fetch API provides a powerful and convenient way to make HTTP calls in JavaScript. But how does it compare to alternatives like Axios and native Node.js modules?

Features

FeatureFetch APIAxiosNode HTTP
Promise-based
Interceptors
Streams
Timeout handling
Cancellation
Browser support

Fetch – Fully promise-based with wide browser support. Streams but no interceptors or timeout handling.

Axios – Promise-based with advanced features like interceptors, cancellation, and timeout handling. Browser usage requires polyfill.

Node HTTP – Core HTTP module in Node.js with streams and timeouts. Callback-based.

Performance

Fetch API performance is comparable to Axios for basic usage according to benchmarks.

However, Axios may be faster when using some advanced features due to its optimizations. Node HTTP can be the fastest when pipelining requests.

Use Cases

Fetch – Great for browser HTTP requests and integration with frontend. Also easy to use on the server.

Axios – Full-featured for robust HTTP requests from Node.js. Excellent for API clients.

Node HTTP – When performance and low-level control is critical. Can utilize keep-alive connections.

So in summary, Fetch API hits a nice sweet spot between simplicity and features.

Conclusion

I hope this guide has provided a comprehensive overview of making HTTP requests with Fetch API in Node.js. Some key takeaways:

  • Fetch provides an easy and modern API for network requests using Promises
  • It supports all common HTTP methods like GET, POST, PUT, DELETE
  • Request headers, body, timeouts, redirects can be configured via the options
  • Responses are streamed with helper methods like .text(), .json() for parsing
  • Fetch is similar to Axios but without some advanced features like interceptors
  • For basic HTTP calls from Node.js or browser, Fetch API is an excellent choice

Fetch API usage will continue grow, especially with support now in Node.js itself. The simple yet powerful API encourages cleaner async code compared to callbacks or XHR.

I hope you found this guide helpful! Let me know if you have any other Fetch API questions.

Join the conversation

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