Kala Tech

ΚΑΛΑ • GOOD TECH

Reactive Data Fetching with Angular httpResource

Angular's reactivity model has evolved rapidly since the introduction of Signals. While we've seen signal-based inputs, outputs, and computed states, data fetching was still largely bound to RxJS Observables via HttpClient.

With the introduction of the httpResource API, Angular provides a native, signal-based mechanism to fetch data from HTTP endpoints reactively. Let's explore how it works and how it simplifies your frontend code.


The Old Way: RxJS & HttpClient

Traditionally, fetching data reactively based on a parameter (like a query parameter or search query) required piping observables together, managing subscriptions, or using the async pipe in templates:

import { Component, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { BehaviorSubject, switchMap } from 'rxjs';

@Component({
  selector: 'app-legacy-fetch',
  template: `
    <div *ngIf="user$ | async as user; else loading">
      Hello, {{ user.name }}
    </div>
    <ng-template #loading>Loading...</ng-template>
  `
})
export class LegacyFetchComponent {
  private http = inject(HttpClient);
  private userId$ = new BehaviorSubject<string>('123');

  // Manual RxJS chain to fetch when user ID changes
  user$ = this.userId$.pipe(
    switchMap(id => this.http.get<any>(`/api/users/${id}`))
  );
}

While powerful, this pattern introduces observable boilerplate and subscription management complexity.


The New Way: Using httpResource

The httpResource API bridges the gap by wrapping HTTP requests into a declarative, signal-based primitive. It automatically tracks any signals accessed inside its URL definition. When those signals change, it triggers a new HTTP request automatically.

Here is the basic syntax:

import { Component, signal } from '@angular/core';
import { httpResource } from '@angular/common/http';

@Component({
  selector: 'app-modern-fetch',
  template: `
    @if (userResource.isLoading()) {
      <p>Loading...</p>
    } @else if (userResource.error()) {
      <p>Error: Failed to fetch user.</p>
    } @else {
      <p>Hello, {{ userResource.value()?.name }}</p>
    }
  `
})
export class ModernFetchComponent {
  // A reactive signal parameter
  userId = signal('123');

  // httpResource automatically re-fetches when userId changes!
  userResource = httpResource(() => `/api/users/${this.userId()}`);
}

Key Features of httpResource

1. Automatic Reactive Dependencies

Because the API accepts a function, Angular establishes dependency tracking. If you reference this.userId(), this.searchQuery(), or any other signal, httpResource automatically schedules a fetch whenever any of those signals emit a new value.

2. Eager Invocation

Unlike traditional HttpClient observable calls (which do not make a request until someone subscribes to them), httpResource is eager. The request is initiated automatically as soon as the component is initialized, matching user expectations for component state fetching.

3. Integrated State Signals

The object returned by httpResource exposes several utility signals directly:


Advanced Request Configurations

If you need to customize headers, pass query parameters, or specify HTTP methods, you can return a request object from the function instead of a simple URL string:

import { Component, signal } from '@angular/core';
import { httpResource } from '@angular/common/http';

@Component({
  selector: 'app-advanced-fetch',
  template: `...`
})
export class AdvancedFetchComponent {
  userId = signal('123');
  includeDetails = signal(true);

  userResource = httpResource(() => ({
    url: `/api/users/${this.userId()}`,
    method: 'GET',
    headers: {
      'Authorization': 'Bearer token_abc'
    },
    params: {
      'details': this.includeDetails() ? 'full' : 'basic'
    }
  }));
}

Since the request object itself is resolved dynamically, changes to includeDetails() will automatically trigger a new fetch with updated query parameters.


Best Practices and Limitations

  1. Read-Only/GET Requests: httpResource is designed for fetching read-only data (queries). For mutations (creating, updating, or deleting resources), continue using the standard HttpClient inject method to make manual imperative calls (http.post(...)).
  2. Under-the-hood HttpClient: Under the hood, httpResource utilizes your application's configured HttpClient. This means all of your existing HTTP Interceptors (e.g., for attaching auth tokens or handling global errors) will continue working seamlessly.

Conclusion

The httpResource utility is a major step forward for signal-based applications, allowing us to build deeply reactive layouts with zero RxJS boilerplate. Try upgrading your query-fetching services today to experience a cleaner, subscription-free codebase!