Skip to main content

Command Palette

Search for a command to run...

HTTP Clients: Axios vs Fetch vs Got

Learn: HTTP Clients: Axios vs Fetch vs Got

Updated
5 min readView as Markdown
T

Welcome to TopperBlog! 👋

I'm a tech content creator passionate about helping developers level up their careers and master cutting-edge technologies.

🎯 What I Write About: • AI/ML Engineering & LLMs • Web3 & Blockchain Development
• System Design & Architecture • Interview Preparation (FAANG) • Freelancing & Remote Work • Modern Tech Stacks (Next.js, React, Rust, TypeScript) • Performance Optimization & Best Practices

💼 Mission: Sharing practical, actionable insights that accelerate your tech career and maximize your earning potential.

📚 15+ In-Depth Guides covering everything from earning $10k/month as a freelancer to cracking FAANG interviews.

🌐 Let's connect and grow together in this amazing tech journey!

#TechBlogger #SoftwareEngineering #CareerGrowth #WebDevelopment #AIEngineering

HTTP Clients: Axios vs Fetch vs Got

Problem

Modern JavaScript applications need to make HTTP requests to APIs and servers. Developers must choose between multiple options: the native Fetch API, Axios library, and Got library. Each has different strengths, weaknesses, and use cases.

Solution

Understanding the key differences helps you select the right tool:

  • Fetch: Native browser/Node.js API, minimal overhead, requires manual handling
  • Axios: Feature-rich, automatic transformations, great DX, extra bundle size
  • Got: Node.js focused, streams support, retries built-in, lightweight

Detailed Comparison

1. Fetch API

Pros:

  • Native to browsers and Node.js 18+
  • No dependencies
  • Lightweight
  • Modern Promise-based API

Cons:

  • No request/response interceptors
  • Manual JSON serialization
  • No built-in timeout
  • No automatic retries
  • Verbose error handling
// Basic GET request
async function fetchExample() {
  try {
    const response = await fetch('https://api.example.com/users');

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

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

// POST with headers and body
async function fetchPost() {
  const response = await fetch('https://api.example.com/users', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer token123'
    },
    body: JSON.stringify({ name: 'John', email: 'john@example.com' })
  });

  return response.json();
}

// Timeout implementation (manual)
function fetchWithTimeout(url, options = {}, timeout = 5000) {
  return Promise.race([
    fetch(url, options),
    new Promise((_, reject) =>
      setTimeout(() => reject(new Error('Timeout')), timeout)
    )
  ]);
}

// Usage
fetchWithTimeout('https://api.example.com/users', {}, 3000)
  .then(res => res.json())
  .catch(err => console.error(err));

2. Axios

Pros:

  • Request/response interceptors
  • Automatic JSON transformation
  • Request cancellation
  • Timeout support built-in
  • Works in browser and Node.js
  • Great error handling
  • Request/response transformation

Cons:

  • Additional dependency
  • Larger bundle size (~13KB)
  • Overkill for simple requests
import axios from 'axios';

// Create instance with defaults
const apiClient = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 5000,
  headers: {
    'Content-Type': 'application/json'
  }
});

// Request interceptor
apiClient.interceptors.request.use(
  config => {
    config.headers.Authorization = `Bearer ${getToken()}`;
    console.log('Request:', config);
    return config;
  },
  error => Promise.reject(error)
);

// Response interceptor
apiClient.interceptors.response.use(
  response => {
    console.log('Response:', response.data);
    return response.data;
  },
  error => {
    if (error.response?.status === 401) {
      // Handle unauthorized
      refreshToken();
    }
    return Promise.reject(error);
  }
);

// GET request
async function getUsers() {
  try {
    const users = await apiClient.get('/users');
    return users;
  } catch (error) {
    console.error('Error fetching users:', error.message);
  }
}

// POST request
async function createUser(userData) {
  try {
    const response = await apiClient.post('/users', userData);
    return response;
  } catch (error) {
    if (error.response) {
      console.error('Server error:', error.response.status);
    } else if (error.request) {
      console.error('No response received');
    } else {
      console.error('Error:', error.message);
    }
  }
}

// Request cancellation
const controller = new AbortController();

async function fetchWithCancel() {
  try {
    const response = await apiClient.get('/users', {
      signal: controller.signal
    });
    return response;
  } catch (error) {
    if (error.name === 'CanceledError') {
      console.log('Request cancelled');
    }
  }
}

// Cancel request
controller.abort();

// Parallel requests
async function fetchMultiple() {
  try {
    const [users, posts] = await Promise.all([
      apiClient.get('/users'),
      apiClient.get('/posts')
    ]);
    return { users, posts };
  } catch (error) {
    console.error('Error:', error);
  }
}

3. Got

Pros:

  • Lightweight and fast
  • Built-in retry logic
  • Stream support
  • Hooks system
  • Pagination support
  • Better for Node.js
  • Smaller bundle size (~5KB)

Cons:

  • Node.js only (not browser)
  • Smaller ecosystem
  • Less popular than Axios
import got from 'got';

// Create instance with defaults
const apiClient = got.extend({
  prefixUrl: 'https://api.example.com',
  timeout: { request: 5000 },
  headers: {
    'user-agent': 'MyApp/1.0'
  },
  retry: { limit: 2 },
  hooks: {
    beforeRequest: [
      options => {
        options.headers.authorization = `Bearer ${getToken()}`;
      }
    ],
    afterResponse: [
      (response, retryWithMergedOptions) => {
        if (response.statusCode === 401) {
          refreshToken();
          return retryWithMergedOptions({ headers: { authorization: `Bearer ${getToken()}` } });
        }
        return response;
      }
    ]
  }
});

// GET request
async function getUsers() {
  try {
    const users = await apiClient.get('users').json();
    return users;
  } catch (error) {
    console.error('Error:', error.message);
  }
}

// POST request
async function createUser(userData) {
  try {
    const response = await apiClient.post('users', {
      json: userData
    }).json();
    return response;
  } catch (error) {
    console.error('Error:', error.response?.statusCode);
  }
}

// Stream support
async function downloadFile(url, destination) {
  try {
    const stream = got.stream(url);
    stream.pipe(fs.createWriteStream(destination));

    stream.on('error', error => {
      console.error('Stream error:', error);
    });
  } catch (error) {
    console.error('Error:', error);
  }
}

// Pagination
async function getAllUsers() {
  try {
    const users = [];
    for await (const item of apiClient.paginate('users', {
      pagination: { limit: 10 }
    })) {
      users.push(item);
    }
    return users;
  } catch (error) {
    console.error('Error:', error);
  }
}

// Retry with exponential backoff
const response = await apiClient.get('users', {
  retry: {
    limit: 3,
    methods: ['GET', 'PUT', 'HEAD', 'DELETE', 'OPTIONS', 'TRACE'],
    statusCodes: [408, 413, 429, 500, 502, 503, 504],
    errorCodes: ['ETIMEDOUT', 'ECONNRESET', 'EADDRINUSE', 'ECONNREFUSED', 'EPIPE', 'ENOTFOUND', 'ENETUNREACH', 'EAI_AGAIN']
  }
}).json();

Comparison Table

FeatureFetchAxiosGot
EnvironmentBrowser/Node.jsBrowser/Node.jsNode.js only
Bundle Size0KB (native)~13KB~5KB
Interceptors✅ (hooks)
Timeout
Retries
Streams
JSON Auto
Cancellation
Learning CurveEasyMediumMedium
PopularityHighVery HighMedium

Decision Guide

Use Fetch when:

  • Building browser-only applications
  • Minimizing dependencies
  • Simple API calls without complex logic
  • You don't need interceptors

Use Axios when:

  • Building full-stack applications
  • Need interceptors for auth/logging
  • Want automatic JSON transformation
  • Working with complex request/response handling
  • Browser compatibility is important

Use Got when:

  • Building Node.js-only applications
  • Need built-in retry logic
  • Working with streams
  • Want lightweight solution
  • Building CLI tools or backend services

Real-World Example: API Client

// Universal approach using Axios
class APIClient {
  constructor(baseURL, token) {
    this.client = axios.create({
      baseURL,
      timeout: 10000
    });

    this.client.interceptors.request.use(config => {
      if (token) config.headers.Authorization = `Bearer ${token}`;
      return config;
    });

    this.client.interceptors.response.use(
      res => res.data,
      error => {
        const message = error.response?.data?.message || error.message;
        throw new Error(message);
      }
    );
  }

  async get(endpoint, params) {
    return this.client.get(endpoint, { params });
  }

  async post(endpoint, data) {
    return this.client.post(endpoint, data);
  }

  async put(endpoint, data) {
    return this.client.put(endpoint, data);
  }

  async delete(endpoint) {
    return this.client.delete(endpoint);
  }
}

// Usage
const api = new APIClient('https://api.example.com', 'token123');
const users = await api.get('/users', { page: 1 });

Conclusion

  • Fetch: Best for simple browser applications
  • Axios: Best for complex applications needing features
  • Got: Best for Node.js backend services

Choose based on your specific needs, environment, and complexity requirements.