# HTTP Clients: Axios vs Fetch vs Got

# 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

```javascript
// 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

```javascript
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

```javascript
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

| Feature | Fetch | Axios | Got |
|---------|-------|-------|-----|
| **Environment** | Browser/Node.js | Browser/Node.js | Node.js only |
| **Bundle Size** | 0KB (native) | ~13KB | ~5KB |
| **Interceptors** | ❌ | ✅ | ✅ (hooks) |
| **Timeout** | ❌ | ✅ | ✅ |
| **Retries** | ❌ | ❌ | ✅ |
| **Streams** | ✅ | ❌ | ✅ |
| **JSON Auto** | ❌ | ✅ | ✅ |
| **Cancellation** | ✅ | ✅ | ✅ |
| **Learning Curve** | Easy | Medium | Medium |
| **Popularity** | High | Very High | Medium |

## 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

```javascript
// 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.
