HTTP Requests in Node.js with Fetch API

In this guide, you will learn how to make HTTP requests in Node.js with Fetch API
10 min read
Fetch API in NodeJS

The Fetch API represents the new officially supported way to perform HTTP requests and retrieve local resources in Node.js. This means that you no longer need external HTTP client dependencies in your project. All you have to do is learn how to use the Node Fetch API, which is what this guide is all about.

Here, you will see:

  • What is the Fetch API
  • Getting started with Node Fetch API
  • Making HTTP requests in Node.js with the Fetch API
  • Extra options and features

Let’s dive in!

What Is the Fetch API?

The Fetch API is a JavaScript interface for fetching resources locally or over a network. In detail, it provides a global fetch() function that makes it easier to perform asynchronous HTTP requests. The same method can also be used to retrieve local files. The JavaScript Fetch API is a flexible replacement for the legacy XMLHttpRequest API. 

The fetch() method is based on the Request and Response objects. It requires only one mandatory argument, the local path or URL to the resource you want to fetch. Then, it also accepts some optional options, including CORS, HTTP header, and caching settings. As an asynchronous method, fetch() returns a Promise that resolves the response produced by the server. This is represented by a Response object, which exposes several methods to access and parse its content body.

This is what a basic Fetch API call looks like:

fetch("https://httpbin.io/ip", {

  // optional configs...

}).then((response) => {

  // print the JSON response body

  console.log(response.json()) // {  "origin": "<YOUR_IP_ADDRESS>" }

})

Or if you prefer the equivalent async/await syntax, you can write:

const response = await fetch("https://httpbin.io/ip", {

  // optional configs...

})

// print the JSON response body

console.log(await response.json()) // { "origin": "<YOUR_IP_ADDRESS>" }

Getting Started with Node Fetch API

The Fetch API has been supported by major browsers for years. Yet, it has only been part of the standard Node.js library since version 18.0.0, released in April 2022. Specifically, the Node Fetch API is based on undici’s implementation. 

Before Node.js 18, you could use fetch() by enabling it as an experimental feature or thanks to the node-fetch npm library, another popular Fetch API implementation. Since fetch() is now part of the official Node.js standard library, you can use it directly in your code without importing it. All you have to do is call the fetch() method with the syntax below:

fetch(url, options)

url is mandatory and can contain:

  • The path to a local resource (e.g., movies.json) 
  • The URL to a remote endpoint or resource (e.g., https://httpbin.io/ip or https://example.com/movies.json)

options is instead an optional object that accepts the following optional fields:

  • method: The HTTP method of the request, such as “GET”, “POST”, “PUT”, “PATCH”, and “DELETE”. The default is “GET”. 
  • headers: A Headers or object literal containing the HTTP headers to add to your request. By default, no header is set.
  • body: The object containing the data to use as the body of your request. Note that GET and HEAD requests cannot have a body.
  • mode: The mode to use for the request (e.g., “cors”, “no-cors”, “same-origin”, “navigate”, or “websocket”). By default, it is set to cors.
  • credentials: To specify whether the browser should send the credentials or not. It must be one of the following strings: “omit”, “same-origin”, or “include”.
  • redirect: To determine how to handle an HTTP redirect response. It can be “follow”, “error”, or “manual”. By default, it is set to “follow”.
  • referrer: A string containing the referrer of the request. By default, it is an empty string.
  • referrerPolicy: Specifies the referrer policy to use for the request.
  • signal: An AbortSignal object instance that allows you to abort the request via the AbortController interface.
  • priority: A string that specifies the priority of the current Fetch request relative to other requests of the same type. It accepts “high”, “low”, or “auto”. By default, it is “auto”.

Check out the fetch() parameters section from the official documentation to learn more.

This is an example of a Node.js Fetch request with an options object:

const response = await fetch("https://your-domain.com/api/v1/users", {

  method: "POST",

  credentials: "include",

  headers: {

    "Content-Type": "application/json",

  },

  body: JSON.stringify({

    username: "jane-doe",

    email: "[email protected]"

    role: "superuser",

    age: 23,

    birthplace: "New York",

  }),

})

Note that the body data must match the Content-Type header.

Making HTTP Requests in Node.js With the Fetch API

Let’s now see the Node Fetch API in action in real-world request examples for the most popular HTTP methods.

GET

This is how you can perform a GET request with the Fetch API:

const response = await fetch("https://your-domain.com/your-endpoint")

As you can see, it takes only one line of code. That is because fetch() performs GET requests by default.

Then, you can access the response content with one of the methods below:

  • response.text(): Returns a Promise that resolves with the response body as text.
  • response.json(): Returns a Promise that resolves with an object parsed from the JSON response.
  • response.blob(): Returns a Promise that resolves with the response body as a Blob object.
  • response.arrayBuffer(): Returns a Promise that resolves with the response body as an ArrayBuffer instance.
  • response.formData(): Returns a promise that resolves with the response body as a FormData object.

So, the code of a complete example would be:

const response = await fetch("https://httpbin.io/ip")

const jsonResponseContent = await response.json() // { "origin": "<YOUR_IP_ADDRESS>" }

const origin = jsonResponseContent.origin // <YOUR_IP_ADDRESS>

If the response returned by the server is not in JSON format, the response.json() instruction will fail with a SyntaxError.

POST

Making a POST request with a Node Fetch API call only takes a few lines:

const formData = new FormData()

formData.append("username", "serena-smith")

formData.append("email", "[email protected]")

const response = await fetch("https://example.com/api/v1/users", {

  method: "POST",

  body: formData,

})

The key to sending a POST request with fetch() is to specify data to send to the server in the body option. This can be in several formats, including JSON, FormData, and text. When sending a FormData object, you do not need to specify a Content-Type header. Otherwise, it is mandatory. 

PUT

Performing a PUT request with the Fetch API is just like making a POST: 

const response = await fetch("https://example.com/api/v1/users", {

  method: "PUT",

  credentials: "include",

  headers: {

    "Content-Type": "application/json",

  },

  body: JSON.stringify({

    username: "john-doe",

    email: "[email protected]"

    role: "regular-user",

    age: 47,

    birthplace: "Chicago",

  }),

})

The only difference is that you need to specify “PUT” as method setting. Similarly, you can send a PATCH request by setting it to “PATCH”.

DELETE

Here is an example of an HTTP DELETE request with fetch():

const response = await fetch("https://example.com/api/v1/users/45", {

  method: "DELETE",

})

Again, it all comes down to setting the right HTTP method. The Fetch API implementation will take care of the rest.

Extra Options and Features

Now that you know how to use fetch() in common scenarios, you are ready to explore the Node Fetch API’s advanced options.

Setting Headers

fetch() enables you to customize the HTTP headers of your request through the headers field of the options object. In particular, headers accepts a Headers object or an object literal with specific string values.

Suppose you want to set the Content-Type and User-Agent header in your fetch() request. You could either use a Headers object as below:

const customHeaders = new Headers()

customHeaders.append("Content-Type", "application/json")

customHeaders.append("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36")

const response = fetch("https://your-domain.com/your-endpoint", {

  headers: customHeaders,

  // other options...

})

Otherwise, you could equivalently set them with an object literal:

const response = fetch("https://your-domain.com/your-endpoint", {

  headers: {

    "Content-Type": "application/json",

    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36",

  },

  // other options...

})

This syntax is more compact and easier to read.

Reading Headers

If you want to read the HTTP headers set by the server in the response, you can access them as follows:

const response = await fetch("https://your-domain.com/your-endpoint", {

  // optional configs...

})

// accessing the "Content-Type" response header

const responseHeaders = response.headers

const responseContentType = response.headers.get("Content-Type")

The response.headers field returns a Headers object, from which you can access specific headers with the get()  method.

Node Fetch API Error Handling

A Node.js Fetch API call can fail only for two reasons:

  • An AbortError exception: When the request was intentionally aborted by an AbortController.
  • A TypeError exception: This can occur because of several reasons, such as an invalid header name, an invalid URL, or a generic network error. Find out more causes in the docs.

What is essential to understand is that any 4xx or 5xx response is considered a successful request for the Fetch API. In other words, an error response produced by the server will not trigger any JavaScript error. The reason for this behavior is that fetch() made the request and the server replied with a response. Conceptually, that cannot be considered an error from the network’s point of view. At the end of the day, the request ended successfully.

This means that before dealing with the data returned by the server, you should always check for a successful response. To do so, you can implement the error-handling logic below:

try {

  const response = await fetch("https://your-domain.com/your-endpoint", {

    // optional configs...

  })

  if (response.ok) {

    // use the data returned by the server...

    // e.g., 

    // await response.json()

  } else {

    // handle 4xx and 5xx errors...

  }

} catch (error) {

  // handle network or abort errors...

}

Note that the ok property from Response contains true only when the request was successful.

Aborting a Fetch Request

The Fetch API supports the abortion of already initiated requests through the AbortController API. 

To stop an ongoing fetch() request, you first need to generate a signal object as below:

const controller = new AbortController()

const signal = controller.signal

Then, specify it in the options object of your request:

const response = await fetch("https://your-domain.com/your-endpoint", {

  signal: signal,

  // other configs...

})

Now, whenever you call the following instruction, your request will be interrupted by an AbortError:

controller.abort()

Keep in mind that the server might have already received the request. In this case, the server will still execute the request but the response will be ignored by Node.js.

Congrats! You are now a Node.js Fetch API master!

Conclusion

In this article, you learned what is the Fetch API and how to use it in Node.js. In detail, you started from the fetch() basics and then dug into its advanced options and features. With such a powerful HTTP client, retrieving online data becomes easy. For example, you could use it to call our SERP API endpoints and start scraping SERP data.

Want to learn more about scraping with Node.js? Read our web scraping with Node.js guide.