Angular's journey toward fine-grained reactivity has been one of the most significant transformations in modern frontend frameworks. With the introduction of signals in Angular 16 and their subsequent refinement, the framework has been systematically rebuilding its foundations for a more performant, intuitive developer experience. But there was always one piece missing: a native, signal-aware way to handle asynchronous data fetching. Enter the Resource API—the final piece that completes Angular's reactivity puzzle.
The Reactivity Revolution in Angular
Before we dive into Resources, let's understand the context. Angular's shift toward signals represents a fundamental rethinking of change detection and state management. Traditional zone.js-based change detection was powerful but came with performance overhead and sometimes unpredictable behavior. Signals introduced a declarative, fine-grained reactivity model that tracks dependencies automatically and updates only what's necessary.
The pieces of Angular's reactivity system have been falling into place:
- Signals provide reactive primitive values
- Computed signals derive state automatically
- Effects handle side effects reactively
- Signal inputs and outputs make components fully reactive
But asynchronous operations—the lifeblood of modern web applications—remained an awkward fit. Developers were left bridging the gap between promises and signals with manual toSignal() conversions or creating their own patterns.
The Async Data Problem
Fetching data has always been a challenge in reactive systems. You need to handle loading states, error states, success states, retries, and refetching—all while maintaining reactivity. Traditional approaches in Angular involved:
// The old way: mixing observables and signals
data = signal<User | null>(null);
loading = signal(true);
error = signal<Error | null>(null);
constructor() {
this.http.get<User>('/api/user').subscribe({
next: (user) => {
this.data.set(user);
this.loading.set(false);
},
error: (err) => {
this.error.set(err);
this.loading.set(false);
}
});
}
This works, but it's verbose, error-prone, and breaks reactivity chains. If the request parameters change, you need to manually trigger new requests. There's no built-in way to handle dependent data fetching or coordinate multiple async operations.
Enter the Resource API
The Resource API introduces a first-class, signal-native way to handle asynchronous data fetching. It's designed from the ground up to work seamlessly with Angular's reactivity system, providing automatic refetching, loading states, and error handling—all while maintaining the declarative nature of signals.
Here's what the same example looks like with Resources:
userResource = resource({
request: () => ({ id: this.userId() }),
loader: ({ request }) => this.http.get<User>(`/api/users/${request.id}`)
});
That's it. The Resource automatically tracks the userId signal, refetches when it changes, and exposes reactive state for loading, errors, and data.
The Power of Declarative Async
The Resource API's brilliance lies in its declarative nature. You describe what data you need and how to fetch it, and the framework handles the rest. Let's explore the key features:
Automatic Dependency Tracking
Resources automatically track signals used in the request function. When those signals change, the resource refetches automatically:
searchQuery = signal('');
searchResults = resource({
request: () => ({ query: this.searchQuery() }),
loader: ({ request }) =>
this.http.get(`/api/search?q=${request.query}`)
});
When searchQuery changes, searchResults automatically refetches. No manual subscriptions, no lifecycle hooks, no memory leaks.
Built-in State Management
Every resource provides reactive access to its state:
userResource.value() // The loaded data
userResource.isLoading() // Loading state
userResource.error() // Error state
userResource.status() // Overall status
These are all signals themselves, so you can use them in templates, computed signals, and effects:
@Component({
template: `
@if (userResource.isLoading()) {
<app-spinner />
} @else if (userResource.error()) {
<app-error [error]="userResource.error()" />
} @else {
<app-user-profile [user]="userResource.value()" />
}
`
})
Coordinating Multiple Resources
One of the most powerful aspects is how Resources compose. You can create dependent resources that automatically coordinate their loading:
userId = signal(1);
user = resource({
request: () => ({ id: this.userId() }),
loader: ({ request }) =>
this.http.get<User>(`/api/users/${request.id}`)
});
userPosts = resource({
request: () => ({
userId: this.user.value()?.id
}),
loader: ({ request }) =>
request.userId
? this.http.get(`/api/posts?userId=${request.userId}`)
: Promise.resolve([])
});
The userPosts resource waits for user to load, then automatically fetches the posts. Change userId, and both resources refetch in the correct order.
Advanced Patterns
The Resource API supports sophisticated patterns that previously required significant boilerplate:
Manual Refetching
reload() {
this.userResource.reload();
}
Optimistic Updates
async updateUser(updates: Partial<User>) {
const current = this.userResource.value();
// Optimistically update
this.userResource.set({ ...current, ...updates });
try {
await this.http.patch('/api/user', updates);
} catch (error) {
// Revert on error
this.userResource.reload();
}
}
Request Debouncing
searchQuery = signal('');
debouncedQuery = signal('');
effect(() => {
const query = this.searchQuery();
setTimeout(() => this.debouncedQuery.set(query), 300);
});
searchResults = resource({
request: () => ({ query: this.debouncedQuery() }),
loader: ({ request }) => this.searchService.search(request.query)
});
Why This Completes the Puzzle
The Resource API is the missing piece because it extends reactivity into the asynchronous realm in a way that feels native to Angular's signal-based model. Before Resources, you had to break out of the reactive paradigm to fetch data, then manually wire things back together. Now, async data fetching is just another reactive primitive.
This completion of the reactivity puzzle means:
- Fully Declarative Components: Components can be purely declarative, from inputs to async data to computed state
- Automatic Change Detection: No more manual change detection triggers or zone.js overhead
- Better Performance: Fine-grained updates mean only affected parts of the UI rerender
- Simpler Code: Less boilerplate, fewer lifecycle hooks, clearer intent
- Type Safety: Full TypeScript support throughout the async pipeline
The Road Ahead
The Resource API represents Angular's maturation as a signals-first framework. It's not just about performance—though the benefits there are significant—it's about providing developers with a coherent mental model for building reactive applications.
As the Angular team continues refining the Resource API and the broader signals ecosystem, we're seeing a framework that's more intuitive, more performant, and more aligned with how developers actually think about building applications. The reactivity puzzle isn't just complete—it's beautiful.
For teams building modern Angular applications, the Resource API isn't just another tool in the toolbox. It's the bridge that makes Angular's reactive vision fully realized, connecting the synchronous world of signals with the asynchronous reality of web development. And that makes all the difference.
The Resource API is available in Angular 19 and later as a developer preview feature. Check the official Angular documentation for the most up-to-date information and API details.
Top comments (0)