How to Configure Axios in React Native

This guide provides a comprehensive overview of configuring the Axios HTTP client for React Native mobile applications. It covers essential installation steps, setting up a centralized instance, handling authentication tokens via request and response interceptors, and troubleshooting platform-specific networking challenges such as local environment base URLs.

1. Installing Axios

To get started, install Axios in your React Native project using your package manager of choice:

npm install axios
# or
yarn add axios

2. Setting Up a Base Configuration

Creating a centralized Axios instance allows you to define default settings such as your API's base URL, timeout durations, and standard headers.

Create a file named apiClient.js (or apiClient.ts for TypeScript):

import axios from 'axios';
import { Platform } from 'react-native';

// Define local development URLs based on platform
const getBaseUrl = () => {
  if (__DEV__) {
    // Android emulator uses 10.0.2.2; iOS simulator uses localhost
    return Platform.OS === 'android' 
      ? 'http://10.0.2.2:3000/api' 
      : 'http://localhost:3000/api';
  }
  return 'https://api.yourproductiondomain.com/api';
};

const apiClient = axios.create({
  baseURL: getBaseUrl(),
  timeout: 10000,
  headers: {
    'Content-Type': 'application/json',
    Accept: 'application/json',
  },
});

export default apiClient;

3. Configuring Request Interceptors for Authentication

Mobile applications commonly store authentication tokens in secure storage (such as @react-native-async-storage/async-storage or expo-secure-store). Use Axios request interceptors to automatically attach this token to outgoing requests.

import apiClient from './apiClient';
import AsyncStorage from '@react-native-async-storage/async-storage';

apiClient.interceptors.request.use(
  async (config) => {
    try {
      const token = await AsyncStorage.getItem('user_token');
      if (token) {
        config.headers.Authorization = `Bearer ${token}`;
      }
    } catch (error) {
      console.error('Error retrieving auth token:', error);
    }
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);

4. Handling Global Errors with Response Interceptors

Response interceptors allow you to catch network failures, handle expired sessions, or refresh tokens before passing the response back to your components.

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

    if (error.response) {
      // Handle 401 Unauthorized globally
      if (error.response.status === 401 && !originalRequest._retry) {
        originalRequest._retry = true;
        // Perform logout actions or trigger token refresh flow here
      }
    } else if (error.request) {
      // Network error or no response received
      console.error('Network error. Check connection or base URL.');
    } else {
      console.error('Request setup error:', error.message);
    }

    return Promise.reject(error);
  }
);

5. Making API Calls in Components

Import your configured client into your components or services to perform requests directly:

import React, { useEffect, useState } from 'react';
import { View, Text, ActivityIndicator } from 'react-native';
import apiClient from './apiClient';

export default function UserProfile() {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    apiClient.get('/user/profile')
      .then((response) => {
        setUser(response.data);
      })
      .catch((error) => {
        console.error('Failed to fetch user:', error);
      })
      .finally(() => {
        setLoading(false);
      });
  }, []);

  if (loading) return <ActivityIndicator size="large" />;
  if (!user) return <Text>Failed to load profile.</Text>;

  return (
    <View>
      <Text>Welcome, {user.name}</Text>
    </View>
  );
}

6. Key Considerations for Mobile Networking