Using Axios as an Injectable Service in Angular

This guide explains how to integrate the Axios HTTP client into an Angular application by wrapping it inside an injectable service. While Angular provides its built-in HttpClient, developers often prefer Axios for its interceptor handling, automatic JSON transformation, or consistency across non-Angular projects. Below are the direct steps and code examples required to set up Axios, configure a base service, and inject it into Angular components using the dependency injection system.

1. Install Axios

Install Axios in your Angular project via npm or yarn:

npm install axios

2. Create the Axios Service

Generate an Angular service using the Angular CLI:

ng generate service services/axios

Open the newly created axios.service.ts and set up an Axios instance. This instance allows you to configure global settings such as base URLs, headers, and timeouts:

import { Injectable } from '@angular/core';
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';

@Injectable({
  providedIn: 'root',
})
export class AxiosService {
  private axiosInstance: AxiosInstance;

  constructor() {
    this.axiosInstance = axios.create({
      baseURL: 'https://api.example.com',
      timeout: 10000,
      headers: {
        'Content-Type': 'application/json',
      },
    });

    this.setupInterceptors();
  }

  private setupInterceptors(): void {
    // Request Interceptor
    this.axiosInstance.interceptors.request.use(
      (config) => {
        const token = localStorage.getItem('auth_token');
        if (token && config.headers) {
          config.headers.Authorization = `Bearer ${token}`;
        }
        return config;
      },
      (error) => Promise.reject(error)
    );

    // Response Interceptor
    this.axiosInstance.interceptors.response.use(
      (response) => response,
      (error) => {
        // Centralized error handling
        console.error('API Error:', error.response?.status, error.message);
        return Promise.reject(error);
      }
    );
  }

  public get<T>(url: string, config?: AxiosRequestConfig): Promise<AxiosResponse<T>> {
    return this.axiosInstance.get<T>(url, config);
  }

  public post<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<AxiosResponse<T>> {
    return this.axiosInstance.post<T>(url, data, config);
  }

  public put<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<AxiosResponse<T>> {
    return this.axiosInstance.put<T>(url, data, config);
  }

  public delete<T>(url: string, config?: AxiosRequestConfig): Promise<AxiosResponse<T>> {
    return this.axiosInstance.delete<T>(url, config);
  }
}

3. Inject and Use the Service in a Component

With the service decorated with { providedIn: 'root' }, it is immediately available for dependency injection across the application.

You can inject the service into a component using either constructor injection or the inject() function.

Example Using Constructor Injection:

import { Component, OnInit } from '@angular/core';
import { AxiosService } from './services/axios.service';

interface User {
  id: number;
  name: string;
}

@Component({
  selector: 'app-user-list',
  template: `
    <ul>
      <li *ngFor="let user of users">{{ user.name }}</li>
    </ul>
  `,
})
export class UserListComponent implements OnInit {
  users: User[] = [];

  constructor(private axiosService: AxiosService) {}

  async ngOnInit(): Promise<void> {
    try {
      const response = await this.axiosService.get<User[]>('/users');
      this.users = response.data;
    } catch (error) {
      console.error('Failed to load users', error);
    }
  }
}

Example Using the inject() Function:

import { Component, OnInit, inject } from '@angular/core';
import { AxiosService } from './services/axios.service';

@Component({
  selector: 'app-user-profile',
  template: `<div>User details loaded</div>`,
})
export class UserProfileComponent implements OnInit {
  private axiosService = inject(AxiosService);

  async ngOnInit(): Promise<void> {
    try {
      const response = await this.axiosService.get('/profile');
      console.log(response.data);
    } catch (error) {
      console.error('Error fetching profile', error);
    }
  }
}

Structuring Axios this way ensures that request configurations, authentication headers, and error handling remain modular, testable, and maintainable throughout your Angular application.