Stackify is now BMC. Read theBlog

Getting Started With Node Fetch

By: Stackify Team
  |  January 27, 2025
Getting Started With Node Fetch

Node Fetch is a module that adds the Fetch API to Node.js, enabling developers to make HTTP requests the same way they do in a browser. Node Fetch provides a promise-based interface for network operations, which is important for server-side applications that interact with external APIs or services.

Introduction to Node Fetch

Node Fetch connects server-side code with external data sources in Node.js. The module allows developers to fetch data from third-party APIs, communicate with microservices, and retrieve resources for server-side rendering, therefore simplifying these tasks.

Importance of Node Fetch in Modern Web Development

Here’s why Node Fetch is important for modern web development:

  • Easy HTTP requests Sends requests to websites or APIs just like a browser does
  • Clean code – Uses promises, making asynchronous code easier to understand and manage without complex callbacks
  • Lightweight – Fast and efficient without slowing down applications
  • Modern web features – Handles features like CORS for secure resource sharing
  • Built in (Node.js 17.5+) – The Fetch API is included in newer Node.js versions reduces the need for external libraries. Older versions may still require node-fetch
  • Customizable and extendable – Allows easy customization of requests and adds new functionalities
  • Improved error handling – Provides a better way for managing errors

The module streamlines development processes, maintains clean code, and supports the creation of efficient, scalable applications.

Installation and Setup

Set up is simple and enables you to use the Fetch API on the server side.

Prerequisites

  • Node.js 18+ installed
  • Basic knowledge of JavaScript
  • Code editor (VS Code recommended)
  • Basic understanding of async/await and API calls

Installation Via npm

1. Initialize Your Project

Navigate to your project directory in the terminal and run

```bash
      npm init -y
```

This command creates a package.json file, which is essential for managing your project’s dependencies.

2. Install Node Fetch

Next, run this:

```bash
      npm install node-fetch
```

This command adds the node-fetch module to your project.

Then, verify installation by checking the dependencies in your package.json:

```json
{
    "dependencies": {
        "node-fetch": "^3.3.2"
    }
}
```

This step verifies that Node Fetch has been successfully installed as a project dependency.

3. Import Node Fetch

Now, choose the import method based on your project:

CommonJS

```javascript
const fetch = require('node-fetch');
```

This import statement uses CommonJS syntax.

ES Modules

```javascript
import fetch from 'node-fetch';
```

This import statement uses ES Modules syntax, which requires that your Node.js project use ES Modules.

Basic Setup and Configuration

1. Import Node Fetch

Import the module as shown above.

2. Run a Simple GET Request

```javascript
const fetch = require('node-fetch');
fetch('https://api.example.com/data')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));
```

This code shows a basic GET request that logs the JSON response.

3. Configure Request Options (POST Request with JSON Payload)

```javascript
const fetch = require('node-fetch');
fetch('https://api.example.com/data', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json'
    },
    body: JSON.stringify({ key: 'value' })
})
    .then(response => response.json())
    .then(data => console.log('Success:', data))
    .catch(error => console.error('Error:', error));
```

The code above sends a POST request with a JSON payload, showing how to send data to an API.

Common Issues and Solutions During Installation

  • Cannot find module node-fetch
    • Ensure the module is installed and listed in package.json
    • If you’re using CommonJS and node-fetch v3+, use an older version or enable ES Modules
    • Install type definitions (@types/node-fetch) for TypeScript projects
  • Module syntax errors
    • Use “require” for CommonJS or enable ES Modules by adding “type”:”module” to package.json
  • Compatibility issues
    • Use a compatible Node.js version (12+) or an older Node Fetch version if needed
  • Network issues
    • Clear the npm cache with “npm cache clean – force” and reinstall
  • Conflicting dependencies
    • Review your project’s dependencies to ensure there are no version conflicts
    • Try installing Node Fetch in a fresh project to identify if an issue is specific to the setup of the current project

Core Features of Node Fetch

Fetch API Basics

The Fetch API replaces XMLHttpRequest for handling HTTP requests more efficiently. Node Fetch brings this API to Node.js, allowing network operations on both the client and server side.

Key features include

  • Promise-based: Simplifies asynchronous code management
  • Streaming support: Manages large data transfers without using excessive memory
  • Flexible configuration: Allows customization of headers, methods, and body content
  • Standardization: Ensures code works consistently across different applications

Supported HTTP Methods

Node Fetch supports standard HTTP methods:

1. GET: Retrieve data.

```javascript
fetch('https://api.example.com/users')
  .then(response => response.json())
  .then(users => console.log(users));
```

The code above retrieves a list of users from an API using a GET request.

2. POST: Create new resources.

```javascript
      fetch('https://api.example.com/users', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ name: 'John Doe', email: '[email protected]' })
})
  .then(response => response.json())
  .then(user => console.log(user));
```

The code above sends a POST request to create a new user, including request headers and body.

3. PUT: Update or create resources.

```javascript
fetch('https://api.example.com/users/1', {
  method: 'PUT',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ name: 'Jane Doe', email: '[email protected]' })
})
  .then(response => response.json())
  .then(updatedUser => console.log(updatedUser));
```

This code shows how to use an HTTP PUT request to update a user resource, also including headers and body.

4. DELETE: Remove resources.

```javascript
fetch('https://api.example.com/users/1', { method: 'DELETE' })
  .then(response => {
    if (response.ok) {
      console.log('User deleted.');
    }
  })
  .catch(error => console.error('Error:', error));
```

The code above shows how to use an HTTP DELETE request to remove a user from the server.

5. HEAD, PATCH, OPTIONS: You can use these methods for specialized tasks.

Handling Responses and Errors

Proper management of responses and errors is important when building applications.

Status Codes

Now, let’s check the response status using response.ok and response.status.

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

This code shows how to check for HTTP error status codes and parse the response as JSON.

Some best practices for handling responses and errors include:

  • Parsing response bodies
    • JSON: Use “response.json().”
    • Text: Use “response.text().”
    • Stream: Use “response.body.pipe(process.stdout).”
  • Managing errors
    • Use “.catch()” to handle network errors
    • Manually check response status for HTTP errors

Advanced Usage of Node Fetch

Customizing Requests

You can customize your HTTP requests with specific headers, authentication, or configurations.

Customizing Headers

```javascript
const fetch = require('node-fetch');
const options = {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
    'Accept': 'application/json',
    'Custom-Header': 'CustomValue'
  }
};
fetch('https://api.example.com/data', options)
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));
```

This code configures request headers, including authorization, content type, and a custom header.

Customizing Request Body

```javascript
const fetch = require('node-fetch');
const userData = { name: 'Jane Doe', email: '[email protected]' };
const options = {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(userData)
};
fetch('https://api.example.com/users', options)
    .then(response => response.json())
    .then(data => console.log('User Created:', data))
    .catch(error => console.error('Error:', error));
```

This code sends a POST request with a JSON payload, showing how to include data in the request body.

Timeout Configuration Customization

```javascript
const fetch = require('node-fetch');
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5000);
fetch('https://api.example.com/slow-endpoint', { signal: controller.signal })
    .then(response => response.json())
    .then(data => {
      clearTimeout(timeout);
      console.log(data);
    })
    .catch(error => {
      if (error.name === 'AbortError') {
        console.error('Request timed out');
      } else {
        console.error('Error:', error);
      }
    });
```

This code shows how to set a timeout for fetch requests using AbortController.

Handling Redirects

```javascript
const fetch = require('node-fetch');
const options = { method: 'GET', redirect: 'follow' };
fetch('http://api.example.com/redirect', options)
    .then(response => {
        if (response.redirected) {
            console.log('Redirected to:', response.url);
        }
        return response.json();
    })
    .then(data => console.log(data))
    .catch(error => console.error('Error:', error));
```

This code demonstrates how to handle HTTP redirects with Node Fetch.

Proxy Configuration Customization

```javascript
const fetch = require('node-fetch');
const HttpsProxyAgent = require('https-proxy-agent');
const proxyUrl = 'http://proxy.example.com:8080';
const agent = new HttpsProxyAgent(proxyUrl);
const options = { method: 'GET', agent };
fetch('https://api.example.com/data', options)
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));
```

This code shows how to configure a proxy for your fetch request using the HttpsProxyAgent package.

Handling Cookies and Sessions

Node Fetch doesn’t include built-in cookie management, but it can be extended with libraries like tough-cookie and fetch-cookie. Let’s take a look:

```javascript
const fetch = require('node-fetch');
const tough = require('tough-cookie');
const { CookieJar } = tough;
const { default: fetchCookie } = require('fetch-cookie');
const jar = new CookieJar();
const fetchWithCookies = fetchCookie(fetch, jar);

const login = async () => {
  const response = await fetchWithCookies('https://api.example.com/login', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ username: 'user', password: 'pass' })
  });
  return response.json();
};

const fetchProtectedData = async () => {
  const response = await fetchWithCookies('https://api.example.com/protected', {
    method: 'GET'
  });
  return response.json();
};

(async () => {
  try {
    const loginData = await login();
    console.log('Logged in:', loginData);
    const protectedData = await fetchProtectedData();

    console.log('Protected Data:', protectedData);
  } catch (error) {
    console.error('Error:', error);
  }
})();
```

This example uses tough-cookie and fetch-cookie to manage sessions with Node Fetch.

Streaming With Node Fetch

Streaming is important for handling large payloads.

```javascript
const fetch = require('node-fetch');
const fs = require('fs');
const dest = fs.createWriteStream('./large-file.zip');
fetch('https://api.example.com/large-file')
  .then(response => {
    if (!response.ok) {
      throw new Error(`Failed to fetch: ${response.statusText}`);
    }
    response.body.pipe(dest);
  })
  .catch(error => console.error('Error:', error));
```

This code streams a response body to a file for efficient handling of large data transfers.

Common Use Cases

Interacting With REST APIs

Node Fetch is ideal for interacting with REST APIs and supporting CRUD operations.

1. Fetching Resources (GET)

```javacript
const fetch = require('node-fetch');
const getProducts = async () => {
  try {
    const response = await fetch('https://api.example.com/products');
    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }
    const products = await response.json();
    console.log('Products:', products);
  } catch (error) {
    console.error('Error fetching products:', error);
  }
};
getProducts();
```

This code shows how to fetch a list of products from an API endpoint using GET.

2. Creating Resources (POST)

```javascript
const fetch = require('node-fetch');
const addProduct = async (product) => {
  try {
    const response = await fetch('https://api.example.com/products', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(product),
    });
    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }
    const newProduct = await response.json();
    console.log('New Product Added:', newProduct);
  } catch (error) {
    console.error('Error adding product:', error);
  }
};
const product = { name: 'Wireless Mouse', price: 29.99, category: 'Electronics' };
addProduct(product);
```

The code above shows how to create a new product resource using a POST request, including the required headers and body.

3. Updating Resources (PUT)

```javascript
const fetch = require('node-fetch');
const updateProduct = async (productId, updatedData) => {
  try {
    const response = await fetch(`https://api.example.com/products/${productId}`, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(updatedData),
});
    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }
    const updatedProduct = await response.json();
    console.log('Product Updated:', updatedProduct);
  } catch (error) {
    console.error('Error updating product:', error);
  }
};
const updatedData = { price: 24.99, stock: 150 };
updateProduct(101, updatedData);
```

This code example uses a PUT request to update a product; headers and body are provided.

4. Deleting Resources (DELETE)

```javascript
const fetch = require('node-fetch');
const deleteProduct = async (productId) => {
  try {
    const response = await fetch(`https://api.example.com/products/${productId}`, {
      method: 'DELETE',
    });
    if (response.ok) {
      console.log(`Product with ID ${productId} deleted.`);
    } else {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }
  } catch (error) {
    console.error('Error deleting product:', error);
  }
};
deleteProduct(101);
```

The code above shows how to delete a specific product using a DELETE request.

Fetching Data from External APIs

Node Fetch can easily retrieve data from various external APIs. Here are some examples:

1. Weather Data (Using OpenWeatherMap)

const fetch = require('node-fetch');
const getWeather = async (city) => {
  const apiKey = 'YOUR_OPENWEATHERMAP_API_KEY';
  const url = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}`;
  try {
    const response = await fetch(url);
    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }
const weatherData = await response.json();
    console.log(`Weather in ${city}:`, weatherData);
  } catch (error) {
    console.error('Error fetching weather data:', error);
  }
};
getWeather('New York');
```

The code above uses an external API to get the weather data of a specified city.

2. Stock Prices (Using Alpha Vantage Platform)

```javascript
const fetch = require('node-fetch');
const getStockPrice = async (symbol) => {
  const apiKey = 'YOUR_ALPHAVANTAGE_API_KEY';
  const url = `https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=${symbol}&apikey=${apiKey}`;
  try {
    const response = await fetch(url);
    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }
    const stockData = await response.json();
    console.log(`Stock Price for ${symbol}:`, stockData['Global Quote']);
  } catch (error) {
    console.error('Error fetching stock data:', error);
  }
};
getStockPrice('AAPL');
```

The code above uses an external API to get the stock price of a company using an API key.

Server-Side Rendering With Node Fetch

Node Fetch is important for server-side rendering (SSR) by fetching necessary data to render full HTML pages.

1. Fetching Data for Templates (Express.js + EJS)

```javascript
const express = require('express');
const fetch = require('node-fetch');
const app = express();
app.set('view engine', 'ejs');

app.get('/dashboard', async (req, res) => {
  try {
    const response = await fetch('https://api.example.com/user/data');
    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }
    const userData = await response.json();
    res.render('dashboard', { user: userData });
  } catch (error) {
    console.error('Error fetching user data:', error);
    res.status(500).send('Internal Server Error');
  }
});
app.listen(3000, () => console.log('Server is running on port 3000'));
```

The code above uses Node Fetch to retrieve data before rendering a page using EJS and Express.js.

2. SEO Optimization

```javascript
const express = require('express');
const fetch = require('node-fetch');
const app = express();

app.get('/', async (req, res) => {
    try {
        const response = await fetch('https://api.example.com/homepage/content');
        if (!response.ok) {
            throw new Error(`HTTP error! Status: ${response.status}`);
        }
        const content = await response.json();
        res.send(`
            <!DOCTYPE html>
            <html>
            <head>
                <title>Home Page</title>
                <meta name="description" content="${content.metaDescription}">
            </head>
            <body>
                <h1>${content.title}</h1>
                <p>${content.body}</p>
            </body>
            </html>
        `);
    } catch (error) {
        console.error('Error fetching homepage content:', error);
        res.status(500).send('Internal Server Error');
    }
});
app.listen(3000, () => {
    console.log('Server is running on port 3000');
});
```

The code above provides dynamic meta descriptions for SEO improvements by making an API call before rendering the page.

Comparison With Other Fetch Libraries

Node Fetch vs. Axios

FeatureNode FetchAxios
InstallationLightweightLightweight
Promise-basedYesYes
Browser compatibilityPrimarily Node.jsNode.js and browsers
Automatic JSON parsingManual parsing requiredAutomatic JSON parsing
InterceptorsNot built inSupports request/response interceptors
Error handlingBasic, manual checksEnhanced, rejects on non-2xx status
Request cancellationAbortControllerCancellation tokens
File operationsManual handlingSimplified file handling

When to Choose Axios or Node Fetch

Choose Axios if you need built-in interceptors, automatic JSON parsing, and enhanced error handling. Choose Node Fetch if you prefer a lightweight library, more control, and consistency with the Fetch API.

Node Fetch vs. native-fetch-pkg

FeatureNode Fetchnative-fetch-pkg
InstallationLightweightLightweight
Promise-basedYesYes
Browser compatibilityPrimarily Node.jsPrimarily Node.js
Automatic JSON parsingManual parsing requiredManual parsing required
InterceptorsNot built inNot built in
Error handlingBasic, manual checksBasic, manual checks
Request cancellationAbortControllerAbortController
Community/maintenanceLarge, activeSmaller, less active

When to Choose Axios or Node Fetch

Choose Node Fetch if you prefer a well-supported library. Choose native-fetch-pkg if you prefer a minimalistic approach.

Pros and Cons of Using Node Fetch

Pros

  • Lightweight and minimalistic
  • Consistent across environments
  • Promise-based interface
  • Extensible
  • Streaming support
  • Good for observability tools

Cons

  • No built-in features like interceptors
  • Manual error handling
  • Smaller ecosystem
  • No automatic JSON transformation

Error Handling in Node Fetch

Common Errors and Status Codes

  • HTTP errors (400 to 500): Handle these by checking response.ok or response.status.
  • Network errors: Use .catch() to handle network connectivity issues.
  • AbortError: Handle cases when the request is aborted using AbortController.

Implementing Retry Logic

```javascript
const fetch = require('node-fetch');
const fetchWithRetry = async (url, options = {}, retries = 3, backoff = 300) => {
  try {
    const response = await fetch(url, options);
    if (!response.ok) {
        if (response.status >= 400 && response.status < 500) {
            throw new Error(`HTTP error! Status: ${response.status}`);
        }
        throw new Error(`HTTP error! Status: ${response.status}`);
    }
    return await response.json();
  } catch (error) {
    if (retries > 0) {
      console.warn(`Retrying... Attempts left: ${retries}. Error: ${error.message}`);
      await new Promise(res => setTimeout(res, backoff));
      return fetchWithRetry(url, options, retries - 1, backoff * 2);
    } else {
      console.error('Max retries reached. Error:', error);
      throw error;
    }
  }
};

const getData = async () => {
  try {
    const data = await fetchWithRetry('https://api.example.com/data', {}, 3, 500);
    console.log('Data retrieved successfully:', data);
  } catch (error) {
    console.error('Failed to fetch data after retries:', error);
  }
};
getData();
```

This code provides an example of Fetch with retry logic using exponential backoff.

Best Practices for Error Handling

  • Differentiate between client, server, and network errors
  • Implement centralized error handling
  • Use environment variables for configuration
  • Provide graceful degradation and feedback
  • Integrate with observability tools
  • Validate and sanitize inputs

Application Monitoring

Integrating monitoring tools like Stackify Retrace provides insights into your application’s behavior and helps quickly resolve issues when using Node Fetch for HTTP requests.

Monitoring with Stackify Retrace

Stackify Retrace is an application monitoring tool that tracks and optimizes applications. Integrating Stackify with Node Fetch helps you monitor HTTP requests in real time, identifying and addressing problems faster.

Key Features

  1. Transaction monitoring: Records each Node Fetch request’s URL, response time, status, and payload to find slow or failed requests
  2. Error logging: Logs errors with stack traces and context for easy debugging
  3. Performance metrics: Tracks latency, throughput, and error rates in the dashboard
  4. Integration: Connects with development tools, setting alerts for important issues

Conclusion

Node Fetch simplifies HTTP requests in Node.js by using the standard Fetch API, a promise-based interface, and streaming support. Although Node Fetch requires manual handling for tasks like JSON parsing and error management, its lightweight design provides a foundation for building scalable applications.

Start your free Retrace trial today to improve observability and enhance your application’s performance and reliability. Combining Stackify Retrace with Node Fetch unlocks insights, optimizes performance, and delivers better user experiences.

Improve Your Code with Retrace APM

Stackify's APM tools are used by thousands of .NET, Java, PHP, Node.js, Python, & Ruby developers all over the world.
Explore Retrace's product features to learn more.

Learn More

Want to contribute to the Stackify blog?

If you would like to be a guest contributor to the Stackify blog please reach out to [email protected]