Skip to main content

HTTP Requests in Node.js: fetch, node-fetch, axios, and got

Created: August 4, 2018 Larry Qu 5 min read

Introduction

Node.js 18+ inch, axios, and got` are the popular choices. This guide covers all options with practical examples. See Javascript Guide for more context. See Javascript Guide for more context.

Native fetch (Node.js 18+)

Since Node.js 18, fetch is available globally — no installation needed:

// GET request
const response = await fetch('https://api.github.com/users/github');

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

const user = await response.json();
console.log(user.name);

// POST request
const.example.com/users', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ name: 'Alice', email: '[email protected]' }),
});

const created = await response.json();

Check your Node.js version:

node --version  # need 18.0.0 or higher for native fetch

node-fetch (Node.js < 18)

For older Node.js versions, node-fetch provides the same Fetch API:

npm install node-fetch

// ESM (package.json: “type”: “module”) import fetch from ’node-fetch';

// CommonJS const fetch = require(’node-fetch’);


The API is identical to native `fetch` — the same code works with both.

### Fetching Text and JSON

import fetch from ’node-fetch';

// Fetch HTML/text const response = await fetch(‘https://example.com/'); const html = await response.text(); console.log(html.slice(0, 200));

// Fetch JSON const res = await fetch(‘https://api.github.com/users/github'); new Error(HTTP error: ${res.status}); const data = await res.json(); console.log(data.login, data.public_repos);

### POST with JSON Body

const response = await fetch(‘https://api.example.com/login', { method: ‘POST’, headers: { ‘Content-Type’: ‘application/json’, ‘Accept’: ‘application/json’, }, body: JSON.stringify({ email: ‘[email protected]’, password: ‘secret’ }), });

if (!response.ok) { const error = await response.json().catch(() => ({})); throw new Error(error.message || HTTP ${response.status}); }

const { token } = await response.json();

### Handling HTTPS with Self-Signed Certificates

In development, you may need to bypass certificate verification:

import https from ‘https’; import fetch from ’node-fetch’;

// Only use in development — never in production const agent = new https.Agent({ rejectUnauthorized: false });

Resources

const response = await fetch(‘https://api.example.com/upload', { method: ‘POST’, body: form, // Don’t set Content-Type — fetch sets it with boundary });

## Which Should You Use?

- **Node.js 18+, simple use case:** native `fetch` — zero dependencies
- **Need interceptors, wide ecosystem:** `axios`
- **Need retry, streaming, TypeScript:** `got`
- **Node.js < 18, want fetch API:** `node-fetch`out(r, 1000 * (i + 1))); // backoff
    }
}

Parallel Requests

// All at once
const [users, posts] = await Promise.all([
    fetch('/api/users').then(r => r.json()),
    fetch('/api/posts').then(r => r.json()),
]);

// With axios
const [usersRes, postsRes] = await Promise.all([
    api.get('/users'),
    api.get('/posts'),
]);

File Upload

import { FormData, Blob } from 'node-fetch';  // node-fetch v3
import fs from 'fs';

const form = new FormData();
form.append('f out after ${timeoutMs}ms`);
        }
        throw err;
    }
}

Retry Logic

async function fetchWithRetry(url, options = {}, retries = 3) {
    for (let i = 0; i < retries; i++) {
        try {
            const response = await fetch(url, options);
            if (response.ok) return response;
            if (response.status < 500) return response; // don't retry 4xx
        } catch (err) {
            if (i === retries - 1) throw err;
        }
        await new Promise(r => setTimeCommon Patterns

### Timeout with AbortController (fetch/node-fetch)

async function fetchWithTimeout(url, timeoutMs = 5000) { const controller = new AbortController(); const id = setTimeout(() => controller.abort(), timeoutMs);

try {
    const response = await fetch(url, { signal: controller.signal });
    clearTimeout(id);
    return response;
} catch (err) {
    clearTimeout(id);
    if (err.name === 'AbortError') {
        throw new Error(`Request timed user  = await api.post('users', { json: { name: 'Alice' } }).json();
## Comparison

| Feature | native fetch | node-fetch | axios | got |
|---|---|---|---|---|
| Node.js 18+ built-in | Yes | No | No | No |
| Browser compatible | Yes | No | Yes | No |
| Automatic JSON | No (manual) | No (manual) | Yes | Yes (`.json()`) |
| Interceptors | No | No | Yes | Hooks |
| Retry built-in | No | No | No | Yes |
| TypeScript | Good | Good | Excellent | Excellent |
| Bundle size | 0 | Small | Medium | Medium |

## ,
}).json();

// With options
const data = await got('https://api.example.com/data', {
    headers: { Authorization: `Bearer ${token}` },
    searchParams: { page: 1, limit: 20 },
    timeout: { request: 5000 },
    retry: { limit: 3 },
}).json();

got Instance

const api = got.extend({
    prefixUrl: 'https://api.example.com',
    headers: { Authorization: `Bearer ${token}` },
    timeout: { request: 10000 },
    retry: { limit: 2 },
});

const users = await api.get('users').json();
const received');
        } else {
            // Request setup error
            console.error('Request error:', error.message);
        }
    }
}

got

got is a modern, feature-rich HTTP client with excellent TypeScript support:

npm install got
import got from 'got';

// GET JSON
const data = await got('https://api.github.com/users/github').json();

// POST
const response = await got.post('https://api.example.com/users', {
    json: { name: 'Alice', email: '[email protected]' }s', { name: 'Alice' });

axios Error Handling

try {
    const response = await axios.get('/api/users/999');
} catch (error) {
    if (axios.isAxiosError(error)) {
        if (error.response) {
            // Server responded with error status
            console.error('Status:', error.response.status);
            console.error('Data:', error.response.data);
        } else if (error.request) {
            // Request made but no response (network error)
            console.error('No response 'application/json' },
});

// Add auth token to every request
api.interceptors.request.use((config) => {
    config.headers.Authorization = `Bearer ${getToken()}`;
    return config;
});

// Handle 401 globally
api.interceptors.response.use(
    (response) => response,
    (error) => {
        if (error.response?.status === 401) {
            redirectToLogin();
        }
        return Promise.reject(error);
    }
);

// Use the instance
const users = await api.get('/users');
const user  = await api.post('/user'https://api.example.com/users', {
    name: 'Alice',
    email: '[email protected]',
});
console.log(response.data);

// With config
const response = await axios.get('https://api.example.com/data', {
    headers: { Authorization: `Bearer ${token}` },
    params: { page: 1, limit: 20 },  // adds ?page=1&limit=20
    timeout: 5000,
});
const api = axios.create({
    baseURL: 'https://api.example.com',
    timeout: 10000,
    headers: { 'Content-Type':json();

Production alternative: Set the NODE_EXTRA_CA_CERTS environment variable to point to your CA certificate file instead of disabling verification.

axios

axios is the most popular HTTP client for Node.js and browsers. It has a cleaner API than fetch for many use cases:

npm install axios
import axios from 'axios';

// GET
const { data } = await axios.get('https://api.github.com/users/github');
console.log(data.name);

// POST
const response = await axios.post(const response = await fetch('https://localhost:8443/api/data', { agent });
const data = await response.

Comments

Share this article

Scan to read on mobile

👍 Was this article helpful?