Axios Secondary API Host Fallback Techniques

Implementing a secondary host fallback strategy in Axios ensures high availability for applications by redirecting traffic to backup servers when the primary host is unreachable or returns server errors. This article covers practical techniques to achieve automated failover using Axios response interceptors, custom client wrappers, and specialized retry libraries, along with key considerations like request idempotency and infinite loop prevention.


1. Axios Response Interceptors

Using Axios interceptors is the most common and native way to handle host fallbacks. When a request fails due to a network error, timeout, or specific HTTP status code (such as 502, 503, or 504), the response interceptor catches the error, updates the target URL or baseURL to the secondary host, and replays the request.

import axios from 'axios';

const PRIMARY_HOST = 'https://api.primary.example.com';
const SECONDARY_HOST = 'https://api.secondary.example.com';

const apiClient = axios.create({
  baseURL: PRIMARY_HOST,
  timeout: 5000,
});

apiClient.interceptors.response.use(
  (response) => response,
  async (error) => {
    const originalRequest = error.config;

    // Check if the request has already failed over
    if (originalRequest && !originalRequest._isRetry) {
      const isNetworkError = !error.response;
      const isServerError = error.response && error.response.status >= 500;

      if (isNetworkError || isServerError) {
        originalRequest._isRetry = true;
        
        // Swap to the secondary host
        originalRequest.baseURL = SECONDARY_HOST;
        
        // Re-execute the request with the updated config
        return apiClient(originalRequest);
      }
    }

    return Promise.reject(error);
  }
);

export default apiClient;

2. Using axios-retry with Dynamic URL Configuration

The axios-retry library simplifies retry logic and allows dynamic modification of request parameters on subsequent attempts.

import axios from 'axios';
import axiosRetry from 'axios-retry';

const PRIMARY_HOST = 'https://api.primary.example.com';
const SECONDARY_HOST = 'https://api.secondary.example.com';

const apiClient = axios.create({
  baseURL: PRIMARY_HOST,
  timeout: 5000,
});

axiosRetry(apiClient, {
  retries: 1,
  retryCondition: (error) => {
    return axiosRetry.isNetworkOrIdempotentRequestError(error) || 
           (error.response && error.response.status >= 500);
  },
  onRetry: (retryCount, error, requestConfig) => {
    // Switch to the fallback host on retry
    requestConfig.baseURL = SECONDARY_HOST;
  },
  retryDelay: () => 0, // Failover immediately or apply backoff
});

export default apiClient;

3. Custom API Client Wrapper

A service wrapper pattern encapsulates primary and secondary Axios instances, giving full programmatic control over fallback behavior, circuit breaking, and telemetry logging without mutating raw Axios configs.

import axios from 'axios';

class ResilientApiClient {
  constructor(primaryUrl, secondaryUrl) {
    this.primaryClient = axios.create({ baseURL: primaryUrl, timeout: 5000 });
    this.secondaryClient = axios.create({ baseURL: secondaryUrl, timeout: 5000 });
  }

  async request(config) {
    try {
      return await this.primaryClient(config);
    } catch (error) {
      if (this.shouldFallback(error)) {
        return await this.secondaryClient(config);
      }
      throw error;
    }
  }

  shouldFallback(error) {
    return !error.response || error.response.status >= 500;
  }

  get(url, config) {
    return this.request({ ...config, method: 'get', url });
  }

  post(url, data, config) {
    return this.request({ ...config, method: 'post', url, data });
  }
}

export const api = new ResilientApiClient(
  'https://api.primary.example.com',
  'https://api.secondary.example.com'
);

Key Implementation Considerations