httpResource (v20+)
📚 What is httpResource?
httpResource() is the HTTP-specific version of resource():
- Built-in HTTP: No need to write fetch/HttpClient logic
- Reactive URL: URL can be signal-based, auto-refetches
- Type-safe: Generic type parameter for response
- States: loading, resolved, error states built-in
- SSR support: Works with server-side rendering
🎯 Interview Questions
- Q1: What is the difference between httpResource and HttpClient?
- A: httpResource is declarative and signal-based. It auto-refetches when URL signal changes. HttpClient is imperative, you manually call methods.
- Q2: How do you handle errors with httpResource?
- A: Check resource.status() === 'error' and resource.error() for the error details.
- Q3: Does httpResource need HttpClient setup?
- A: Yes, you still need provideHttpClient() in app config. httpResource uses HttpClient internally.
- Q4: Can you pass custom headers with httpResource?
- A: Yes, use the headers option in httpResource config.
🔧 httpResource Concept Demo
User Lookup (Simulated)
In real httpResource, changing userId signal would auto-fetch!
📋 httpResource API
httpResource<T>({ url })Basic GET request
httpResource<T>({ url, method: 'POST', body })POST with body
resource.value()The response data (Signal)
resource.status()'idle' | 'loading' | 'resolved' | 'error'
resource.isLoading()Boolean shortcut for loading state
resource.error()Error details if failed
resource.reload()Manually trigger refetch
💻 httpResource Code Examples
import { httpResource } from '@angular/common/http';
import { signal, computed } from '@angular/core';
@Component({...})
export class UserComponent {
// Reactive parameter
userId = signal(1);
// ===== Basic httpResource (GET) =====
userResource = httpResource<User>({
url: () => `/api/users/${this.userId()}`
});
// In template:
// @if (userResource.isLoading()) {
// <spinner />
// }
// @if (userResource.value(); as user) {
// {{ user.name }}
// }
// @if (userResource.error(); as error) {
// {{ error.message }}
// }
// ===== Static URL =====
allUsers = httpResource<User[]>({
url: '/api/users'
});
// ===== With Headers =====
protectedResource = httpResource<Data>({
url: '/api/protected',
headers: {
'Authorization': 'Bearer token123',
'Accept': 'application/json'
}
});
// ===== POST Request =====
createUser = httpResource<User>({
url: '/api/users',
method: 'POST',
body: () => ({
name: this.newUserName(),
email: this.newUserEmail()
})
});
// ===== With Query Parameters =====
searchTerm = signal('');
page = signal(1);
searchResults = httpResource<SearchResult[]>({
url: () => `/api/search?q=${this.searchTerm()}&page=${this.page()}`
});
// ===== Using Resource State =====
// Computed based on resource
userCount = computed(() => this.allUsers.value()?.length ?? 0);
isUserLoaded = computed(() =>
this.userResource.status() === 'resolved'
);
// ===== Manual Reload =====
refresh() {
this.userResource.reload();
}
// ===== Error Handling =====
handleError() {
const error = this.userResource.error();
if (error) {
console.error('Failed to load:', error.message);
// error.status - HTTP status code
// error.statusText - Status text
}
}
}
// ===== Setup in app.config.ts =====
import { provideHttpClient, withFetch } from '@angular/common/http';
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient(withFetch()), // Required!
// Or without fetch: provideHttpClient()
]
};
// ===== Comparison: HttpClient vs httpResource =====
// HttpClient (imperative)
@Component({...})
export class OldWay {
private http = inject(HttpClient);
user = signal<User | null>(null);
loading = signal(false);
error = signal<Error | null>(null);
loadUser(id: number) {
this.loading.set(true);
this.http.get<User>(`/api/users/${id}`).subscribe({
next: user => {
this.user.set(user);
this.loading.set(false);
},
error: err => {
this.error.set(err);
this.loading.set(false);
}
});
}
}
// httpResource (declarative)
@Component({...})
export class NewWay {
userId = signal(1);
// Everything handled automatically!
user = httpResource<User>({
url: () => `/api/users/${this.userId()}`
});
// Change userId and it auto-refetches
changeUser(id: number) {
this.userId.set(id); // That's it!
}
}