Skip to content

Aurelia HTTP Client with Fetch and HttpClient

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about Aurelia HTTP Client with Fetch and HttpClient. We cover key concepts, practical examples, and best practices to help you master this topic.

Aurelia's HttpClient builds on the Fetch API to provide a clean, injectable service for making HTTP requests with configurable interceptors and error handling.

What You'll Learn

  • Configuring and injecting HttpClient
  • Making GET, POST, PUT, and DELETE requests
  • Adding request and response interceptors
  • Handling HTTP errors gracefully
  • Cancelling in-flight requests

Why It Matters

Nearly every web application communicates with a backend server. Aurelia's HttpClient abstracts away boilerplate and provides a consistent API that integrates naturally with the Dependency Injection system.

Real-World Use

A dashboard application that fetches real-time metrics from a REST API, sends user updates via POST, and displays appropriate loading states and error messages for each request.

HttpClient Architecture

flowchart LR
    A[Aurelia Component] --> B[HttpClient]
    B --> C[Request Interceptors]
    C --> D[Fetch API]
    D --> E[Backend Server]
    E --> F[Response Interceptors]
    F --> G[Component Data]
    style B fill:#e6f3ff,stroke:#4a90d9,stroke-width:2px

Installing and Configuring HttpClient

First install the fetch client plugin:

npm install aurelia-fetch-client

Register the plugin in your main configuration:

import { PLATFORM } from 'aurelia-pal';

export function configure(aurelia) {
  aurelia.use
    .standardConfiguration()
    .plugin(PLATFORM.moduleName('aurelia-fetch-client'));

  aurelia.start().then(() => aurelia.setRoot());
}

Creating an Injectable API Service

import { autoinject } from 'aurelia-framework';
import { HttpClient } from 'aurelia-fetch-client';

@autoinject
export class ApiService {
  constructor(private http: HttpClient) {
    http.configure(config => {
      config
        .withBaseUrl('https://api.example.com/')
        .withDefaults({
          headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json'
          }
        })
        .withInterceptor({
          request(request) {
            console.log(`Sending request to ${request.url}`);
            return request;
          },
          response(response) {
            console.log(`Received ${response.status} from ${response.url}`);
            return response;
          }
        });
    });
  }
}

Expected output: Every HTTP request is logged to the console with its URL, and every response is logged with its status code.

Making GET Requests

export class UserList {
  users: User[] = [];
  loading = false;
  error = null;

  constructor(private api: ApiService) {}

  async loadUsers(): Promise<void> {
    this.loading = true;
    this.error = null;
    try {
      const response = await this.api.http.fetch('users');
      this.users = await response.json();
    } catch (err) {
      this.error = 'Failed to load users. Please try again.';
    } finally {
      this.loading = false;
    }
  }
}

Expected output: The users array is populated with JSON data from the API, or an error message is displayed if the request fails.

POST, PUT, and DELETE Requests

// POST - Create a resource
async createUser(user: User): Promise<User> {
  const response = await this.http.fetch('users', {
    method: 'POST',
    body: JSON.stringify(user)
  });
  return response.json();
}

// PUT - Update a resource
async updateUser(id: number, user: User): Promise<User> {
  const response = await this.http.fetch(`users/${id}`, {
    method: 'PUT',
    body: JSON.stringify(user)
  });
  return response.json();
}

// DELETE - Remove a resource
async deleteUser(id: number): Promise<void> {
  await this.http.fetch(`users/${id}`, {
    method: 'DELETE'
  });
}

Expected output: createUser returns the newly created resource with its server-assigned ID. updateUser returns the updated resource. deleteUser resolves successfully with no content.

Adding Request and Response Interceptors

Interceptors let you centralize cross-cutting concerns like authentication:

http.configure(config => {
  config.withInterceptor({
    request(request) {
      const token = localStorage.getItem('auth_token');
      if (token) {
        request.headers.append('Authorization', `Bearer ${token}`);
      }
      return request;
    },
    responseError(error) {
      if (error.status === 401) {
        // Redirect to login page
        window.location.hash = '#/login';
      }
      return Promise.reject(error);
    }
  });
});

Expected output: All outgoing requests automatically include the Authorization header. If the server returns 401, the user is redirected to the login page.

Cancelling Requests with AbortController

export class SearchComponent {
  private abortController: AbortController | null = null;

  async search(query: string): Promise<void> {
    // Cancel previous request
    if (this.abortController) {
      this.abortController.abort();
    }

    this.abortController = new AbortController();

    try {
      const response = await this.http.fetch(`search?q=${query}`, {
        signal: this.abortController.signal
      });
      this.results = await response.json();
    } catch (err) {
      if (err.name === 'AbortError') {
        console.log('Previous search cancelled');
        return;
      }
      throw err;
    }
  }
}

Expected output: When the user types quickly, only the final search request completes. Previous requests are cancelled and do not update the results.

Common Mistakes

  1. Not checking response.ok - The Fetch API does not reject on HTTP error statuses (4xx, 5xx). Always check response.ok or use an interceptor to throw on errors.

  2. Forgetting to configure withBaseUrl - Without a base URL, every request must include the full absolute URL, making code brittle when the API endpoint changes.

  3. Stringifying request body for GET - GET requests should not have a body. Only use body with POST, PUT, and PATCH methods.

  4. Not handling loading states - Users may be confused if there is no visual feedback during long requests. Always toggle a loading indicator.

  5. Memory leaks from uncancelled requests - Components that unmount while a request is in flight can cause state updates on destroyed components. Cancel requests when the component is deactivated.

Practice Questions

  1. How do you set a base URL for all HttpClient requests?
  2. What method do you call to run code before every request and after every response?
  3. How does Aurelia's HttpClient differ from using the native Fetch API directly?
  4. What property of the Response object should you check to verify a request succeeded?
  5. How can you cancel an in-flight HTTP request in Aurelia?

Challenge: Build a typeahead search component that fetches results from an API as the user types. Cancel the previous request each time the input changes, and display a loading indicator while waiting for the response.

FAQ

Is HttpClient required for making HTTP requests in Aurelia?

No, you can use the native Fetch API directly. However, HttpClient integrates with Aurelia's DI system, provides configuration helpers, and supports interceptors out of the box.

Can I use HttpClient with other data formats like FormData or Blob?

Yes, HttpClient works with any body type supported by the Fetch API, including FormData for file uploads and Blob for binary data.

How do I handle request timeouts with HttpClient?

Aurelia does not have a built-in timeout config. Use a promise race with AbortSignal.timeout(ms) or wrap the fetch call in a timeout helper.

Does HttpClient support request retries?

Not natively. Implement retry logic in an interceptor by catching response errors and re-issuing the request up to a configured limit.

Can I use multiple HttpClient instances with different configurations?

Yes, register multiple instances with different names using the DI container and inject the specific instance you need.

Mini Project

Create a task manager service that communicates with a REST API. Implement CRUD operations for tasks, add an interceptor that logs every request duration, and show loading spinners while requests are in progress.

What's Next

With HTTP communication in place, learn how to validate user input before sending data to the server.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro