How to Make HTTP Requests in Node.js With Fetch API: An In-Depth Guide

Hey there! Making HTTP requests is crucial to working with APIs, sending data, and retrieving information in your Node.js applications. Recently, support for the Fetch API was added to Node.js, providing a modern way to make requests.

In this comprehensive guide, I‘ll explain everything you need to know to work with the Fetch API in Node.js. You‘ll learn:

  • What is Fetch API and why it matters
  • How to use Fetch for basic and advanced requests
  • Fetch response handling and error catching
  • Fetch vs other Node.js HTTP clients
  • When to use Fetch and things to look out for

By the end, you‘ll be able to use the Fetch API in your Node.js apps to make requests and power your applications! Let‘s dive in.

What is the Fetch API and Why It Matters

The Fetch API provides an interface for making HTTP network requests using JavaScript. Some key aspects:

  • Allows HTTP requests like GET, POST, PUT from JS
  • Uses Promises for async request handling
  • Returns response objects with status, headers, body
  • Designed to unify requests across environments

Fetch was created for web browsers as a modern replacement for XMLHttpRequest (XHR). It has now been adapted for use in Node.js as well.

This is significant because Fetch provides a standard native HTTP client directly within Node.js itself. The popular node-fetch module allowed Fetch use in Node for years.

But first-class support in the platform means Fetch could become the default way to make requests from Node.js – no dependencies required!

Some benefits this offers:

  • Clean and familiar syntax – Using Fetch in Node.js feels just like the browser
  • Promise-based – Enables async/await and avoids callback hell
  • Feature parity – Same capabilities on front and backend
  • Actively maintained – Fetch is improving within Node.js itself

Consider this previous code using XHR:

const xhr = new XMLHttpRequest();
xhr.open(‘GET‘, ‘/example‘);

xhr.onload = () => {
  const resp = xhr.response;
  // ...
}

xhr.onerror = () => {
  console.error(‘Request failed‘);
}

xhr.send();

And the equivalent using the Fetch API:

fetch(‘/example‘)
  .then(resp => {
    // ... 
  })
  .catch(err => {
    console.error(‘Request failed‘); 
  });

Much cleaner and simpler!

The native inclusion of Fetch brings Node.js support in line with the standardized web platform. This creates ample opportunities for increased adoption moving forward.

Using the Fetch API in Node.js

Fetch comes built into Node.js, but is an experimental feature that needs to be enabled first.

To use the Fetch API in Node v17.5+, start your script using the --experimental-fetch flag:

node --experimental-fetch index.js

Now the fetch() method will be available globally. Let‘s look at how to use it!

Making GET Requests

Here‘s an example of using Fetch to GET data from a JSON API:

const fetch = require(‘node-fetch‘);

const url = ‘https://api.example.com/items/1‘;

fetch(url)
  .then(res => res.json())
  .then(data => {
    console.log(data); 
  })
  .catch(err => {
    console.error(err);
  });

We simply pass the URL to fetch() which returns a Promise containing the response. By chaining .then(), we can work with the result async when it resolves.

First we parse res into JSON using res.json(). Then this JSON data can be logged, displayed, or further processed.

That‘s all there is to it! The simple Promise-based interface allows us to ditch callbacks and clearly handle the request flow.

Making POST Requests

Making a POST request with Fetch is just as easy:

const url = ‘/submit-data‘;
const data = { text: ‘Hello World‘ };

fetch(url, {
  method: ‘POST‘,
  body: JSON.stringify(data)
})
.then(res => {
  // Handle response
})
.catch(err => {
  // Handle errors
});

We specify the method option as ‘POST‘ and pass the data as the body. Fetch will serialize the body and set the appropriate headers for us.

For PUT, DELETE, and other methods, the same pattern applies.

According to npm trend data, the node-fetch package for Fetch API has rapidly grown in downloads. It‘s seen over 5 million weekly downloads – evidence of the demand for a simple native Fetch interface.

As Fetch is built-in going forward, this external dependency may reduce over time.

Request Options

Many options can be passed to customize Fetch requests:

fetch(url, {
  method: ‘POST‘,
  headers: {
    Authorization: `Bearer ${token}`
  },
  redirect: ‘manual‘ 
});

Some commonly used options are:

  • method – GET, POST, DELETE, etc
  • headers – Request headers like auth tokens
  • body – Data to send in body
  • redirect – Set redirect mode
  • credentials – Cookies for request
  • cache – Caching policy like no-cache

There are also options to configure timeouts, CORS, gzip compression, and more.

See MDN Web Docs for a full list of supported options.

Overall, the available request options provide complete control. You can fine-tune caching, security, headers, and other behaviors.

Handling Responses

Once a request is made, Fetch returns a Promise containing the response object.

We‘ve already seen using .json() to parse the response body. The response object also gives access to metadata about the request:

fetch(url)
  .then(res => {

    // Response headers
    console.log(res.headers.get(‘Content-Type‘));

    // Status code
    if (res.status === 404) {
      // handle 404
    }

    // Ok?
    if (!res.ok) { 
      // handle failure  
    }

    return res.json();
  })

Important properties include:

  • status – HTTP status code
  • ok – Shorthand for success status 2xx
  • headers – Get response headers
  • url – Final URL after redirects
  • body – Readable stream for body

We can check status and ok within .then() to handle issues before moving forward.

There are also methods to parse body based on the content type:

  • json() – Parse body as JSON
  • text() – Body as plain text
  • blob() – Body as Blob object
  • formData() – Parse body as FormData
  • arrayBuffer() – Body as ArrayBuffer

For example:

fetch(‘/data‘)
  .then(res => res.json())
  .then(data => {
    // use JSON data
  });

Having easy access to metadata and parsed response content makes working with Fetch results straightforward.

Handling Errors

As Fetch uses Promises, any errors are handled by chaining a .catch():

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

Some common errors:

  • Network failure or no internet
  • Incorrect URL leading to a 404
  • Server errors like 500 responses
  • JSON parsing failure if invalid
  • Request timeout

These will reject the Promise, jumping execution to the .catch() block.

For HTTP and API errors, it‘s best to check status and handle specific cases:

fetch(url)
  .then(res => {
    if (res.status === 403) {
      throw new Error(‘Unauthorized‘);
    } 
    // ...
  })
  .catch(err => {
    if (err.message === ‘Unauthorized‘) {
      // handle 403 error
    } else {
      // other error
    }
  });

This allows appropriate behavior for different API response codes returned.

Overall, the Promise interface makes handling asynchronous Fetch requests and errors easy and familiar.

Fetch API vs Axios for HTTP Requests

Axios is a popular third party library that competes with Fetch as an HTTP client for Node.js.

Axios allows making GET, POST, and other requests from Node. It uses Promises and has a similar feel to Fetch.

So which should you use – Fetch or Axios? Here‘s an overview of the differences:

  • Fetch is native while Axios is an external module
  • Axios defaults JSON parsing; Fetch requires res.json()
  • Axios follows Node.js error-first callback style
  • Axios has request/response interceptors; Fetch does not
  • Axios has fancy request cancellation; Fetch lacks this
  • Fetch provides native ES6 interface familiar to frontend devs

Some may prefer Axios for the advanced utility features like interceptors. However, Fetch offers a simpler native API without dependencies.

And as Fetch is built into Node.js itself, it is likely to gain more usage as the go-to API for basic HTTP needs. The node-fetch package is already more popular in downloads.

Here is a real-world code comparison of fetching data from a JSON API:

Axios

const axios = require(‘axios‘);

axios.get(‘/items‘)
  .then(res => {
    console.log(res.data); 
  })
  .catch(err => {
    console.error(err); 
  });

Fetch API

fetch(‘/items‘)
  .then(res => res.json())
  .then(data => {
   console.log(data);
  })
  .catch(err => {
   console.error(err); 
  });

The syntax is very similar, with Fetch looking cleaner to me due to the native Promise approach.

Overall, Fetch is ideal for basic HTTP requests without needing external dependencies or advanced features like interceptors.

But both Axios and Fetch are great options depending on the needs of your application!

Using Fetch for Web Scraping

The Fetch API can be really useful for web scraping from Node.js.

For example, here is code to scrape the title from a Wikipedia page:

const fetch = require(‘node-fetch‘); 
const cherio = require(‘cherio‘);

const url = ‘https://en.wikipedia.org/wiki/Web_scraping‘;

fetch(url)
  .then(res => res.text())
  .then(html => {
    const $ = cherio.load(html);
    const title = $(‘h1#firstHeading‘).text();

    console.log(title);
  });

We fetch the page HTML using Fetch and pass it to Cherio for parsing. This lets us extract the <h1> title text from the document.

Fetch simplifies acquiring HTML content for scraping. Other benefits include:

  • Clean promise-based flow
  • Easy handling of HTML/JSON responses
  • Built-in request options like headers
  • Avoid helper libraries like Request or Axios

Fetch provides a solid foundation for building scrapers. You can focus on data extraction, not HTTP calls.

According to npm trends, Fetch is already the 3rd most downloaded module amongst popular Node HTTP clients – showing its large and growing user base.

When to Use Caution with Fetch

The Fetch API is a handy tool to have, but there are some cautions to be aware of:

  • Fetch is still an experimental feature in Node.js – there may be bugs and the API could change. Be sure to report issues encountered.

  • Browser support is not universal – IE11 and some older browsers lack support without a polyfill.

  • Using Fetch for server-side calls can be problematic depending on configuration. Make sure to handle cookies, CORS, compression correctly.

  • Advanced HTTP features like interceptors, retries, and cancellation are missing or limited compared to Request and Axios.

  • Form data handling requires explicit configuration.

  • Streaming response bodies can be tricky to work with using Fetch.

Overall, Fetch is designed as a minimal viable HTTP client without all the bells & whistles of other libs.

Just be aware of the current limitations and experimental status when using in production apps. And have a backup plan to switch to Axios or Request if needed.

But Fetch certainly looks to be the future of HTTP requests in Node.js!

Putting it All Together

We‘ve covered a lot of ground here. Let‘s quickly recap:

  • The Fetch API provides a modern interface for HTTP requests from JS
  • Fetch is supported natively in Node.js 17.5+ experimentally
  • Easily make GET, POST, PUT, DELETE requests with a clean syntax
  • Fetch uses Promises for async flows and handling errors
  • Configure headers, body, methods, and more via request options
  • Access response data and metadata on response objects
  • Alternative libraries like Axios offer more advanced capabilities
  • Fetch simplifies HTTP calls for use cases like web scraping
  • Monitor for issues as Fetch support continues maturing

Hopefully this gives you a comprehensive overview of how to use the Fetch API within Node.js.

Now you can ditch callback hell and take advantage of promise-based requests and response handling using Fetch.

The native inclusion within Node.js itself makes Fetch highly promising as a simple, standard way to make HTTP calls from JS without dependencies.

Go Make Some Requests!

That wraps up this guide on getting started with the Fetch API in Node.js.

I hope you now feel empowered to:

  • Drop callbacks and embrace simple promise-based HTTP requests
  • Leverage the modern Fetch API in your Node.js backend scripts
  • Confidently handle async flows and errors when working with APIs
  • Build applications by fetching data from the web
  • Move beyond tangled legacy options like XHR

With Fetch support continuing to evolve, there‘s never been a better time to try it out.

I highly recommend playing with examples from this guide yourself to get hands-on with making requests using Fetch.

There are a ton of use cases out there – go build something cool!

And let me know if you have any other questions. Happy fetching!

How useful was this post?

Click on a star to rate it!

Average rating 0 / 5. Vote count: 0

No votes so far! Be the first to rate this post.