Skip to main content

Handling HTTP Requests in Angular: Promises, Observables, Fetch, and Axios

The code for this chapter can be seen in action here.

Comparison of Axios and Fetch

When performing HTTP requests in JavaScript, two common approaches are Axios (a third-party library) and the native Fetch API. Both are used for making asynchronous calls to back-end services, but they differ in their capabilities and ease of use.

Here’s a comparison between Axios and Fetch, focusing on their pros and cons:

Axios vs Fetch

Axios

Axios is a popular third-party HTTP client for making requests in JavaScript. It simplifies many aspects of making HTTP calls and automatically handles many features that you would need to handle manually with Fetch.

Pros of Axios:

  • Simplified Syntax: Axios provides a cleaner and more intuitive API for handling HTTP requests.

    • Example: No need to manually handle JSON responses; Axios automatically parses JSON.
    • Example: Error handling is simpler, since any HTTP status code outside the range of 2xx triggers an error.
  • Automatic JSON Conversion: Axios automatically transforms the response data into JSON, eliminating the need for .json() method.

  • Support for Older Browsers: Axios is compatible with older browsers (including Internet Explorer) without needing polyfills.

  • Interceptors: Axios allows you to set up interceptors, making it easier to handle request and response transformations or logging.

  • Request Cancellation: Axios has built-in support for canceling requests using CancelToken.

  • Progress Tracking: Axios can track progress for both uploads and downloads via event listeners (onUploadProgress, onDownloadProgress).

Cons of Axios:

  • Requires Installation: Being a third-party library, Axios requires installation via npm or yarn.

  • Bigger Bundle Size: Compared to the Fetch API, Axios adds more weight to your bundle, which may be a consideration for performance-sensitive applications.

  • Less Flexibility: Axios has a predefined set of functionality. Although it’s easier to use, it might not cover all edge cases as flexibly as Fetch does.


Fetch API

Fetch is a built-in, promise-based native API that is widely supported in modern browsers. It provides a generic way to handle HTTP requests but leaves many functionalities to the developer to manage manually.

Pros of Fetch:

  • Built-In and No Installation: Since Fetch is a native browser feature, there’s no need to install any additional libraries. This results in a smaller bundle size and fewer external dependencies.

  • Widely Supported in Modern Browsers: Fetch is part of the modern web standard and is supported by almost all browsers (except for some older ones like Internet Explorer, but polyfills are available).

  • Flexible and Configurable: Fetch provides great flexibility, allowing you to control every aspect of the request. You can create highly customized HTTP requests.

Cons of Fetch:

  • Manual JSON Parsing: Fetch doesn’t automatically parse JSON. You need to call .json() on the response object to convert it to JSON.

    • Example:
      const response = await fetch(API_URL);
      const data = await response.json(); // You must manually parse the response
  • Error Handling is More Complex: Fetch only rejects the promise when there’s a network error (like a failure to reach the server). It won’t reject on 4xx or 5xx status codes, so you have to manually check the response status.

    • Example:
      const response = await fetch(API_URL);
      if (!response.ok) {
      throw new Error('HTTP error: ' + response.status);
      }
  • No Request Cancellation: Fetch does not natively support request cancellation. You need to rely on the AbortController API for canceling requests, which adds complexity.

  • No Progress Monitoring: Fetch doesn’t provide built-in support for monitoring the upload or download progress of HTTP requests.


Side-by-Side Comparison

FeatureAxiosFetch
SyntaxSimpler, with automatic JSON parsingMore manual, requires .json() call
Error HandlingRejects on HTTP errors (4xx, 5xx)Must check response.ok manually
Browser SupportWorks with older browsers (e.g., IE)Supported in modern browsers only
InstallationRequires installation (via npm/yarn)Built into the browser
Request CancellationSupports cancellation via CancelTokenRequires AbortController
InterceptorsYes, for request/response interceptionNo native support
Progress TrackingYes, supports upload/download progressNo native support
Handling TimeoutsCustomizable via request configRequires manual timeout implementation
Bundle SizeLarger due to external dependencyMinimal, since it’s built-in

Conclusion: Axios or Fetch?

  • Choose Axios if you prefer a simplified, feature-rich client with built-in error handling, automatic JSON parsing, and progress tracking. It’s well-suited for larger applications where developer convenience and productivity are key.

  • Choose Fetch if you want a lightweight, native solution and are comfortable handling some manual tasks like parsing JSON, managing errors, and request cancellations. Fetch is great for smaller applications or when you want to avoid external dependencies.

Both tools are effective, but the choice between them often depends on the specific requirements of the project and your comfort with managing manual processes.

In Angular, HTTP requests are crucial for communicating with back-end services. You can handle these requests using Promises, Observables, Fetch API, Axios, and Angular's HttpClient service. Below is an overview of each method with examples.

Here's how you can add headers using both Axios and the Fetch API.

Adding Headers with Axios

In Axios, you can easily add headers by passing a headers object in the configuration of the request.

Example: Adding Headers in Axios

import axios from 'axios';

// POST request with headers in Axios
async function addCarAxios(car: Car): Promise<Car> {
const response = await axios.post<Car>(API_URL, car, {
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_TOKEN_HERE' // Example of adding an Authorization header
}
});
return response.data;
}

Adding Headers with Fetch

With Fetch, headers are passed as part of the options object inside the headers property.

Example: Adding Headers in Fetch

// POST request with headers in Fetch
async function addCarFetch(car: Car): Promise<Car> {
const response = await fetch(API_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_TOKEN_HERE' // Example of adding an Authorization header
},
body: JSON.stringify(car)
});
return response.json();
}

Summary of Adding Headers

  • Axios: Headers are passed in the third parameter of the request method (e.g., axios.post()).
  • Fetch: Headers are passed inside the options object under the headers key.

Axios interceptors

In Axios, interceptors are used to handle requests or responses globally before they are processed. This is particularly useful for adding headers (like Authorization) automatically to every request or for handling errors uniformly.

Example: Adding an Authorization Header Using Axios Interceptors

Below is an example where we add an Authorization header to every request using an Axios interceptor. This is especially useful when you have a token (like a JWT) that needs to be sent with every API call.

Axios Interceptor for Request and Response

import axios from 'axios';

// Set up an Axios instance
const axiosInstance = axios.create({
baseURL: API_URL, // Base URL for the API
headers: {
'Content-Type': 'application/json'
}
});

// Add a request interceptor to add the Authorization token to every request
axiosInstance.interceptors.request.use(
(config) => {
const token = 'YOUR_TOKEN_HERE'; // Replace with your dynamic token
if (token) {
config.headers['Authorization'] = `Bearer ${token}`; // Add the token to the headers
}
return config; // Don't forget to return the config
},
(error) => {
// Handle request error
return Promise.reject(error);
}
);

// Add a response interceptor to handle responses globally
axiosInstance.interceptors.response.use(
(response) => {
// Handle the response data if needed
return response;
},
(error) => {
// Handle response error (e.g., global error handling for status codes)
if (error.response.status === 401) {
console.log('Unauthorized - Redirecting to login...');
// Add logic to redirect to login if token is expired
}
return Promise.reject(error);
}
);

// Example function using the axios instance
async function getCarsAxios(): Promise<Car[]> {
const response = await axiosInstance.get<Car[]>('/cars');
return response.data;
}

async function addCarAxios(car: Car): Promise<Car> {
const response = await axiosInstance.post<Car>('/cars', car);
return response.data;
}

Explanation:

  • Request Interceptor: The request interceptor is used to attach the Authorization header to every request. In the config object, we dynamically add the Bearer token if it exists.

  • Response Interceptor: The response interceptor handles responses globally. In this example, it checks if the response status is 401 (Unauthorized) and logs a message, but you can also redirect users to a login page or refresh the token.

Benefits:

  • You don’t have to manually add the Authorization header in every request.
  • You can handle errors (like 401 Unauthorized) consistently across your app.

Promises and Observables

Promises

Promises represent the eventual completion (or failure) of an asynchronous operation and its resulting value. In this case, Axios and Fetch can be used to return Promises.

Observables

In Angular, Observables from the RxJS library are widely used for handling asynchronous data streams, especially in HTTP requests. Observables offer more powerful features than Promises, including the ability to handle multiple values and to cancel subscriptions.


Axios

Axios is a popular HTTP client that returns Promises. Here’s how you can use it in Angular for performing common HTTP operations.

Example: Axios-based HTTP requests

import axios from 'axios';

export class CarService {
async addCarAxios(car: Car): Promise<Car> {
const response = await axios.post<Car>(API_URL, car);
return response.data;
}

async getCarsAxios(): Promise<Car[]> {
const response = await axios.get<Car[]>(API_URL);
return response.data;
}

async getCarByIdAxios(id: number): Promise<Car> {
const response = await axios.get<Car>(`${API_URL}/${id}`);
return response.data;
}

async deleteCarByIdAxios(id: number): Promise<Car> {
const response = await axios.delete<Car>(`${API_URL}/${id}`);
return response.data;
}

async updateCarAxios(id: number, car: Car): Promise<Car> {
const response = await axios.put<Car>(`${API_URL}/${id}`, car);
return response.data;
}

async clearCarsAxios(): Promise<void> {
await axios.delete(API_URL);
}
}

Key Axios Methods:

  • axios.post: Used for sending data (e.g., creating a new car).
  • axios.get: Used for retrieving data (e.g., fetching all cars or a specific car by ID).
  • axios.delete: Used for deleting data (e.g., deleting a car by ID).
  • axios.put: Used for updating data (e.g., updating a car by ID).

Fetch API

The Fetch API is a native browser feature for making HTTP requests. It also returns Promises but doesn't automatically parse the response (you have to call .json() to get the result in JSON format).

Example: Fetch-based HTTP requests

export class CarService {

async addCarFetch(car: Car): Promise<Car> {
const response = await fetch(API_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(car)
});
return response.json();
}

async getCarsFetch(): Promise<Car[]> {
const response = await fetch(API_URL);
return response.json();
}

async getCarByIdFetch(id: number): Promise<Car> {
const response = await fetch(`${API_URL}/${id}`);
return response.json();
}

async deleteCarByIdFetch(id: number): Promise<Car> {
const response = await fetch(`${API_URL}/${id}`, {
method: 'DELETE'
});
return response.json();
}

async updateCarFetch(id: number, car: Car): Promise<Car> {
const response = await fetch(`${API_URL}/${id}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(car)
});
return response.json();
}

async clearCarsFetch(): Promise<void> {
await fetch(API_URL, {
method: 'DELETE'
});
}
}

Key Fetch Methods:

  • fetch(url, options): Performs an HTTP request, where options can specify the method (GET, POST, PUT, DELETE), headers, and body.
  • response.json(): Parses the response body as JSON.

Angular's HttpClient Service

Angular provides a built-in service called HttpClient from the @angular/common/http module, which returns Observables, providing a more powerful and flexible approach to handle HTTP requests.

Example: HttpClient-based HTTP requests

import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

export class CarService {

constructor(private readonly http: HttpClient) { }

addCar(car: Car): Observable<Car> {
return this.http.post<Car>(API_URL, car);
}

getCars(): Observable<Car[]> {
return this.http.get<Car[]>(API_URL);
}

getCarById(id: number): Observable<Car> {
return this.http.get<Car>(`${API_URL}/${id}`);
}

deleteCarById(id: number): Observable<Car> {
return this.http.delete<Car>(`${API_URL}/${id}`);
}

updateCar(id: number, car: Car): Observable<Car> {
return this.http.put<Car>(`${API_URL}/${id}`, car);
}

clearCars(): Observable<void> {
return this.http.delete<void>(API_URL);
}
}

Key HttpClient Methods:

  • http.post: Sends data to the server and returns the created resource.
  • http.get: Retrieves data from the server.
  • http.delete: Deletes a resource on the server.
  • http.put: Updates a resource on the server.

Observables vs Promises

  • Observables: Can emit multiple values over time, are lazy (won’t execute until subscribed), and can be cancelled.
  • Promises: Resolve a single value or an error and execute immediately when called.

Summary

In Angular, you can perform HTTP requests using:

  • Axios: A promise-based HTTP client with simple syntax.
  • Fetch: A native, promise-based API that is more manual but widely supported.
  • Angular HttpClient: A more powerful and flexible tool based on Observables, which integrates seamlessly with Angular's reactive programming features.

Each method offers advantages, but Angular's HttpClient is preferred in most Angular applications because of its native integration and support for Observables.