Ch. 4 · Angular

Angular Resource API for Async Data

Load data reactively with the resource API, track loading and error state as signals, and cancel stale requests automatically.

~2 min readadvancedupdated Oct 5, 2026

The resource API turns an async loader into signals for value, status and error. A request signal describes the inputs, and when it changes the resource reloads and cancels the previous run, which removes the manual cancel-and-ignore pattern that race conditions required.

Before you start

You should be comfortable with signals and async data loading. This article assumes a recent Angular version that exposes resource; the pattern also appears as httpResource for HTTP.

Step-by-step walkthrough

Step 1: Describe the request as a signal

request: () => ({ id: this.id() }) makes the resource reload whenever id changes. Because the request is a signal, there is no imperative load call to forget, and the dependency is explicit.

Step 2: Load with cancellation semantics

The loader receives the request and returns a promise. When the request changes before the previous load finishes, Angular cancels the stale run, so an old response cannot overwrite a newer one. This is the same race protection as keying a fetch, but built into the primitive.

Step 3: Read status and value as signals

value(), status() and error() are signals the template reads. A loading state is status() === 'loading', and an error state reads error(). Because they are signals, change detection updates only the consumers, and derived values such as a retry button can compute from them.

Worked scenario

The resource reloads and cancels when the id changes.

import { signal, resource } from '@angular/core';

const id = signal('1');
const user = resource({
  request: () => ({ id: id() }),
  loader: async ({ request, abortSignal }) => {
    const res = await fetch(`/api/users/${request.id}`, { signal: abortSignal });
    return res.json() as Promise<{ name: string }>;
  },
});
TypeScript

Walk through the example

Reading user.value() yields the loaded user, and changing id triggers a reload with the new request. The abortSignal cancels the previous fetch, so if the user switches ids quickly, only the latest result is applied. The status signal drives a spinner without extra flags.

Common mistake

Calling the loader imperatively or duplicating the request in a useEffect-style pattern, which reintroduces races the resource already prevents. Another is ignoring the abortSignal inside the loader, so the stale fetch keeps running and its result may arrive late.

Verify the behavior

Change the request id twice quickly and assert only the final value appears, proving stale cancellation. Assert status() is loading while in flight and error is populated when the loader rejects. Confirm that a failed load leaves the previous value intact or cleared as you intend.

Interview exercise

How does resource prevent a stale response from overwriting newer data?

Answer and reasoning

When the request signal changes, the resource starts a new run and cancels the previous one through the abortSignal. A cancelled run is discarded, so its late result cannot be applied. This is the reactive equivalent of attaching a request key to each fetch and ignoring responses whose key no longer matches.

Continue learning

Compare fetching patterns in Angular signal derived values and HTTP error handling and retry. Read the Angular resource guide and try the Angular interview questions.

More in Angular

read ✓Angular · hard

Angular Signal Forms Overview

Model forms as signals, bind fields in the template, and reason about validation and derived state in a signal-driven form.

~2 min readread →
read ✓Angular · hard

Angular Zoneless Change Detection

Run Angular without zone.js, drive updates with signals, and understand which async sources still schedule change detection.

~2 min readread →
read ✓Angular · mid

Angular Defer Blocks and Lazy Rendering

Load template regions on demand with @defer, choose a trigger, and use placeholders without delaying critical content.

~2 min readread →
esc