Apidog

All-in-one Collaborative API Development Platform

API Design

API Documentation

API Debugging

API Mocking

API Automated Testing

The Ultimate Guide to Node Fetch API: Making HTTP Requests in Node.js

In this tutorial, we'll explore how to use Node fetch effectively in your projects.

Mark Ponomarev

Mark Ponomarev

Updated on April 12, 2025

HTTP requests are a fundamental part of modern web development. With the introduction of the Fetch API to Node.js, developers now have a powerful and consistent way to make network requests across both browser and server environments. In this comprehensive tutorial, we'll explore how to use Node fetch effectively in your projects.

What is Node Fetch API and Why Should You Use It?

The Node fetch API is a modern, promise-based mechanism for making HTTP requests in Node.js applications. Originally a browser-only feature, fetch became an experimental feature in Node.js v18 and reached stability in Node.js v21.

Key Benefits of Using Node Fetch:

  • Built-in functionality: No need to install third-party packages
  • Promise-based: Clean, modern syntax with async/await support
  • Cross-platform familiarity: Same API as browser-side fetch
  • Improved performance: Built on the high-performance Undici HTTP client

Testing Your Node Fetch API Requests with Modern Tools

While learning to use Node fetch, it's essential to have reliable tools for testing your API endpoints. Apidog stands out as the best Postman alternative for testing and documenting your Node fetch API requests.

As an all-in-one API development platform, Apidog combines API documentation, testing, and mock servers in a single intuitive interface.

When developing applications with Node fetch, Apidog helps you visualize responses, collaborate with team members, and ensure your API calls are working correctly before implementing them in code. Its ability to generate code snippets for Node fetch requests makes the transition from testing to implementation seamless.

button

Setting Up Your Environment for Node Fetch

Prerequisites for Using Node Fetch

Before diving into Node fetch examples, ensure you have:

  1. Node.js v18 or higher (preferably v21+ for stable fetch support)
  2. Check your Node.js version:
node -v

Node Fetch Version Compatibility

  • Node.js v21+: Fetch is stable and ready for production use
  • Node.js v18-v20: Fetch is available but experimental (use -experimental-fetch flag)
  • Older Node.js versions: Install the node-fetch package or upgrade Node.js

If you're using v18-v20, run your applications with:

node --experimental-fetch app.js

Making Your First Node Fetch Request

Let's start with a basic GET request using Node fetch:

// Basic GET request with Node fetch
fetch('<https://api.example.com/data>')
  .then(response => {
    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }
    return response.json(); // Parse JSON response
  })
  .then(data => {
    console.log('Data received:', data);
  })
  .catch(error => {
    console.error('Fetch error:', error);
  });

Using Node Fetch with Async/Await

For cleaner code, you can use async/await with Node fetch:

async function fetchData() {
  try {
    const response = await fetch('<https://api.example.com/data>');

    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }

    const data = await response.json();
    console.log('Data received:', data);
    return data;
  } catch (error) {
    console.error('Fetch error:', error);
  }
}

// Call the function
fetchData();

Advanced Node Fetch Request Methods

Making POST Requests with Node Fetch

async function postData(url, data) {
  try {
    const response = await fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(data),
    });

    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }

    return await response.json();
  } catch (error) {
    console.error('Fetch POST error:', error);
  }
}

// Example usage
const newUser = {
  name: 'John Doe',
  email: 'john@example.com',
};

postData('<https://api.example.com/users>', newUser)
  .then(data => console.log('User created:', data));

PUT Requests with Node Fetch

async function updateData(url, data) {
  try {
    const response = await fetch(url, {
      method: 'PUT',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(data),
    });

    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }

    return await response.json();
  } catch (error) {
    console.error('Fetch PUT error:', error);
  }
}

// Example usage
const updatedUser = {
  id: 1,
  name: 'Jane Smith',
  email: 'jane@example.com',
};

updateData('<https://api.example.com/users/1>', updatedUser)
  .then(data => console.log('User updated:', data));

DELETE Requests with Node Fetch

async function deleteResource(url) {
  try {
    const response = await fetch(url, {
      method: 'DELETE',
    });

    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }

    // Some APIs return no content on DELETE
    if (response.status === 204) {
      return { success: true };
    }

    return await response.json();
  } catch (error) {
    console.error('Fetch DELETE error:', error);
  }
}

// Example usage
deleteResource('<https://api.example.com/users/1>')
  .then(result => console.log('Delete result:', result));

Handling Different Response Types with Node Fetch

Node fetch can work with various response formats:

JSON Response Handling

fetch('<https://api.example.com/data>')
  .then(response => response.json())
  .then(data => console.log(data));

Text Response Handling

fetch('<https://example.com/plain-text>')
  .then(response => response.text())
  .then(text => console.log(text));

Binary Data Handling

fetch('<https://example.com/image.png>')
  .then(response => response.arrayBuffer())
  .then(buffer => {
    // Handle binary data
    const bytes = new Uint8Array(buffer);
    console.log('Binary data length:', bytes.length);
  });

Customizing Node Fetch Requests with Headers and Options

Setting Custom Headers

fetch('<https://api.example.com/protected-data>', {
  headers: {
    'Authorization': 'Bearer YOUR_TOKEN_HERE',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
    'User-Agent': 'My Node.js Application'
  }
})
.then(response => response.json())
.then(data => console.log(data));

Configuring Request Options

fetch('<https://api.example.com/data>', {
  method: 'GET',
  headers: { 'Content-Type': 'application/json' },
  cache: 'no-cache',
  redirect: 'follow',      // follow, error, or manual
  referrerPolicy: 'no-referrer'
})
.then(response => response.json())
.then(data => console.log(data));

Error Handling with Node Fetch

Comprehensive Error Handling

One important aspect to understand about Node fetch is that it doesn't reject on HTTP error status codes. The promise only rejects on network errors or if something prevented the request from completing.

Here's a comprehensive error handling approach:

async function fetchWithErrorHandling(url) {
  try {
    const response = await fetch(url);

    // Check for HTTP errors
    if (!response.ok) {
      // Attempt to get error details from response
      let errorDetails;
      try {
        errorDetails = await response.json();
      } catch (e) {
        errorDetails = await response.text();
      }

      throw new Error(
        `HTTP error! Status: ${response.status}, Details: ${
          typeof errorDetails === 'object'
            ? JSON.stringify(errorDetails)
            : errorDetails
        }`
      );
    }

    return await response.json();
  } catch (error) {
    // Network errors, parsing errors, and our custom HTTP errors
    console.error('Fetch failed:', error.message);
    throw error; // Re-throw to allow calling code to handle
  }
}

Implementing Request Timeout with Node Fetch

Node fetch doesn't have built-in timeout support, but you can implement it using AbortController:

async function fetchWithTimeout(url, options = {}, timeoutMs = 5000) {
  const controller = new AbortController();
  const { signal } = controller;

  // Set up timeout
  const timeout = setTimeout(() => {
    controller.abort();
  }, timeoutMs);

  try {
    const response = await fetch(url, { ...options, signal });
    clearTimeout(timeout); // Clear timeout if fetch completes

    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }

    return await response.json();
  } catch (error) {
    clearTimeout(timeout);
    if (error.name === 'AbortError') {
      throw new Error(`Request timed out after ${timeoutMs}ms`);
    }
    throw error;
  }
}

// Example usage
fetchWithTimeout('<https://api.example.com/data>', {}, 3000)
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error.message));

Handling Authentication with Node Fetch

Basic Authentication

const username = 'user';
const password = 'password';
const credentials = Buffer.from(`${username}:${password}`).toString('base64');

fetch('<https://api.example.com/protected>', {
  headers: {
    'Authorization': `Basic ${credentials}`
  }
})
.then(response => response.json())
.then(data => console.log(data));

Bearer Token Authentication

const token = 'your_jwt_or_oauth_token';

fetch('<https://api.example.com/protected>', {
  headers: {
    'Authorization': `Bearer ${token}`
  }
})
.then(response => response.json())
.then(data => console.log(data));

Best Practices for Using Node Fetch in Production

  1. Always check response status: Don't assume responses are successful
  2. Handle different content types appropriately: Use the correct method for your response type (json(), text(), etc.)
  3. Implement proper error handling: Create utility functions that handle errors consistently
  4. Set request timeouts: Prevent hanging requests with AbortController
  5. Create reusable fetch wrappers: Build a service layer with common request patterns
  6. Consider retry logic for failed requests: Implement exponential backoff for unstable APIs
  7. Use environment variables for base URLs: Keep environment-specific URLs out of code

Common Node Fetch Troubleshooting

"Fetch is not defined" Error

If you encounter ReferenceError: fetch is not defined, check:

  1. You're using Node.js v18+
  2. For Node.js v18-v20, use the -experimental-fetch flag
  3. For older versions, install the node-fetch package

HTTPS Certificate Issues

Node fetch inherits Node's HTTPS certificate handling. For custom certificates:

const https = require('https');
const fs = require('fs');

const httpsAgent = new https.Agent({
  ca: fs.readFileSync('./custom-certificate.pem')
});

fetch('<https://api.example.com/data>', {
  agent: httpsAgent
})
.then(response => response.json())
.then(data => console.log(data));

Conclusion: Embracing Node Fetch in Your Projects

The Node fetch API represents a significant improvement in how we make HTTP requests in Node.js applications. With its promise-based interface, consistent behavior across platforms, and native implementation, it's becoming the preferred choice for modern Node.js development.

By mastering Node fetch, you can create more maintainable code that leverages modern JavaScript features while enjoying improved performance compared to older HTTP client libraries. As the stable implementation continues to mature in Node.js, we can expect even more developers to adopt this powerful API as their standard approach to making HTTP requests.

Now that you have a comprehensive understanding of Node fetch, you're ready to implement it in your own projects and take advantage of this powerful API for all your HTTP request needs.

button

How to Use MAI-DS-R1, Microsoft’s DeepSeek R1 for FreeViewpoint

How to Use MAI-DS-R1, Microsoft’s DeepSeek R1 for Free

Learn how to use MAI-DS-R1, Microsoft’s enhanced DeepSeek R1, for free with this technical guide. Discover step-by-step API setup, Apidog integration, and best practices for leveraging this powerful AI model in your projects.

Ashley Innocent

April 24, 2025

15 Best REST API Clients and Testing Tools for 2025Viewpoint

15 Best REST API Clients and Testing Tools for 2025

This article dives deep into the 15 best REST API clients and testing tools predicted to lead the pack in 2025, helping you select the perfect fit for your needs. We'll explore everything from comprehensive all-in-one platforms to lightweight, focused utilities.

INEZA FELIN-MICHEL

April 23, 2025

Cursor vs IDEA: Why Choose Cursor AI Over JetBrains IntelliJ IDEA?Viewpoint

Cursor vs IDEA: Why Choose Cursor AI Over JetBrains IntelliJ IDEA?

A straightforward comparison of Cursor AI and JetBrains IntelliJ IDEA. We break down the key differences in coding assistance, AI integration, and developer experience to help you decide which IDE is best for your workflow.

Emmanuel Mumba

April 23, 2025