JavaScript Axios

Streamlining API Calls: The Power of Axios Instances and Middleware

In the AplicacionJoyeria project, ensuring robust and consistent interaction with our backend APIs is paramount. We recently implemented a significant improvement in how we manage these interactions, focusing on maintainability and error handling.

The Situation

Before this update, our application's API calls might have been scattered, leading to repetitive code for common tasks such as attaching authorization tokens, setting default headers, or handling global error states. This decentralized approach made it challenging to apply consistent logic across all API requests, leading to increased development time for new features and more complex debugging when issues arose.

The Solution: Centralized API Calls with Axios Middleware

To address these challenges, we introduced a dedicated axios instance configured with request and response interceptors. This pattern effectively creates a middleware layer for all our API communications, centralizing common functionalities.

An axios instance allows us to define a base URL and default headers once, which are then applied to all requests made through that instance. The true power comes from interceptors:

  • Request Interceptors: These functions run before a request is sent. They are ideal for tasks like adding dynamic authorization headers, logging request details, or modifying request parameters.
  • Response Interceptors: These functions run after a response (or error) is received but before it's passed back to the calling code. They are perfect for normalizing response data, automatically refreshing authentication tokens, or handling global error codes (e.g., redirecting users on a 401 Unauthorized status).

Here's an illustrative example of how such an instance and its interceptors might be configured:

import axios from 'axios';

// Create a custom Axios instance
const apiClient = axios.create({
  baseURL: 'https://api.example.com/v1',
  headers: {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  },
  timeout: 10000 // 10 seconds timeout
});

// Add a request interceptor
apiClient.interceptors.request.use(
  (config) => {
    // For example, attach an authorization token from local storage
    const authToken = localStorage.getItem('jwtToken');
    if (authToken) {
      config.headers.Authorization = `Bearer ${authToken}`;
    }
    console.log('Request sent:', config.url);
    return config;
  },
  (error) => {
    // Handle request error
    return Promise.reject(error);
  }
);

// Add a response interceptor
apiClient.interceptors.response.use(
  (response) => {
    // Any successful response can be processed here
    console.log('Response received:', response.config.url);
    return response;
  },
  async (error) => {
    const originalRequest = error.config;

    // Example: Handle 401 Unauthorized errors
    if (error.response.status === 401 && !originalRequest._retry) {
      originalRequest._retry = true; // Prevent infinite retry loops
      try {
        // Logic to refresh token or re-authenticate
        // For now, just log and reject
        console.error('Unauthorized: User needs to re-authenticate.');
        // Optionally, redirect to login page
        // window.location.href = '/login';
        // return await apiClient(originalRequest); // Retry the original request with new token
      } catch (refreshError) {
        // Handle token refresh failure
        return Promise.reject(refreshError);
      }
    }
    console.error('Response error:', error.response?.status, error.message);
    return Promise.reject(error);
  }
);

export default apiClient;

// Usage example:
// apiClient.get('/products').then(response => console.log(response.data));

The Technical Lesson

By leveraging custom axios instances and their interceptors, we achieve a more modular, maintainable, and robust networking layer. This pattern eliminates repetitive code, ensures consistency in request headers and error handling, and significantly simplifies future updates to our API communication strategy. It's a clear demonstration of the DRY (Don't Repeat Yourself) principle in action, making our codebase cleaner and more scalable.

The Takeaway

Centralize your API communication logic using dedicated axios instances and carefully configured request and response interceptors. This practice will dramatically improve the maintainability, consistency, and error handling capabilities of your application's network requests, leading to a more stable and easier-to-debug system.


Generated with Gitvlg.com

J

Johandev

Author

Share: