Angular v20 brings us the httpResource API, built on top of the HttpClient so interceptors and testing tools work better than with the Resource API.
Angular 19 introduced the Resource API for the following purposes:
It should not be used for mutation, such as POST operations. Read more about the resource API here.
However, there was an issue when working with the Resource API. One major problem was that it wasn’t built on top of HttpClient, which meant that interceptors did not function as expected.
To address the above problem, Angular 20 introduces the httpResource API.
The httpResource extends the Resource API by using the HttpClient under the hood, providing a seamless way to make HTTP requests while supporting interceptors and existing testing tools.
You can fetch data using the httpResource API as shown below:
export class App {
constructor() {
effect(() => {
console.log('products', this.products.value());
})
}
products = httpResource<IProduct[]>(() => `http://localhost:3000/product`);
}
You can use products to render a table on the template as shown below.
<table>
<tr>
<th>ID</th>
<th>Name</th>
<th>Price</th>
</tr>
@for(p of products.value(); track p.id) {
<tr>
<td>{{ p.id }}</td>
<td>{{ p.name }}</td>
<td>{{ p.price }}</td>
<td>{{ p.category }}</td>
<td>{{ p.description }}</td>
</tr>
}
</table>
The httpResource API is built on top of the @angular/common/http stack. Any HTTP request made through it will be intercepted by the application’s HTTP interceptor. For example, if you have an interceptor defined like the one below:
import { HttpInterceptorFn } from '@angular/common/http';
export const appInterceptor: HttpInterceptorFn = (req, next) => {
console.log('Request made with URL:', req.url);
return next(req);
};
It should show the output in the browser for the data fetched using the httpResource API. Request made with URL: http://localhost:3000/product.
By default, the httpResource performs a GET request and returns an unknown
typed JSON response.
When using the httpResource API, it’s essential to validate the schema to verify that the received data matches the expected structure. To achieve this, the httpResource API allows the use of schema validation libraries, such as Zod.
To use Zod with the httpResource API, first install it and then import it.
import { z } from 'zod';
After importing, create a schema for the product as shown in the example below.
IProductSchema = z.object({
id: z.number(),
name: z.string(),
price: z.number(),
category: z.string(),
description: z.string().optional()
});
ProductResultsSchema = z.array(this.IProductSchema);
First, we define a schema for the product object, and then the array of products using this schema.
After defining the schema, you can use it to parse the response from the httpResource, as shown below.
products = httpResource(() => ({
url: `http://localhost:3000/product`,
method: 'GET'
}), {
parse: (data) => this.ProductResultsSchema.parse(data)
});
The httpResource API supports a default value. This value is returned when the resource is in one of the following states.
If the default value is not specified, its value is set to undefined. You can set the default value as shown below.
products = httpResource(() => ({
url: `http://localhost:3000/product`,
method: 'GET'
}), {
defaultValue: [
{ id: 0, name: 'default', price: 0, category: 'default', description: 'default' },
],
parse: (data) => this.ProductResultsSchema.parse(data)
});
The key difference between httpResource and HttpClient lies in their request behavior:
Each new request made with the httpResource API automatically cancels the previous one. Because of this behavior, it is not suitable for mutations and should be used only for data fetching.
product = httpResource<IProduct>(() => ({
url: `http://localhost:3000/product/${this.searchId()}`,
method: 'GET'
}));
The httpResource API supports a request object. An httpResource request sent to the backend can include several fields. Among them, only the url
is required—all other fields are optional.
Although the httpResource object supports other verbs, such as POST, PUT, DELETE, etc., it is advisable to use the httpResource API only to fetch data from the backend and not to perform any mutations using other HTTP verbs.
You can pass the httpResource object with value for various properties as shown below:
product = httpResource<IProduct>(() => ({
url: `http://localhost:3000/product/${this.searchId()}`,
method: 'GET',
headers: {
'X-Special': 'true',
},
params: {
'fast': 'yes',
},
reportProgress: true,
withCredentials: true,
transferCache: true,
}));
Like the Resource API, the htppResource API returns a WritableResource
and has read-only properties such as:
All of them are signal types, which means you can track them inside an effect. Besides the above signals, it also returns metadata about the response.
constructor() {
effect(() => {
console.log('products', this.products.value());
console.log('products error', this.products.error()?.message);
console.log('products satus', this.products.status());
})
}
The httpResource API has dedicated methods to work with other response types. They are as follows.
httpResource.text
returns plain texthttpResource.blob
returns the data as a BlobhttpResource.arrayBuffer
returns the data as an ArrayBufferYou can download an image using the httpResource API as shown below:
@Injectable({
providedIn: 'root'
})
export class Product {
getImage(): HttpResourceRef<Blob | undefined> {
return httpResource.blob(() => ({
url: "http://localhost:3000/product/image",
method: 'GET',
reportProgress: true,
}));
}
}
The httpResource API is one of the most exciting features of Angular V20, and you must use it to fetch data from the backend. It uses the HttpClient as a loader and returns various responses as signal. You should avoid using it for data mutation at the backend.
I hope you find this post helpful. Thanks for reading.
Dhananjay Kumar is a well-known trainer and developer evangelist. He is the founder of NomadCoder, a company that focuses on creating job-ready developers through training in technologies such as Angular, Node.js, Python, .NET, Azure, GenAI and more. He is also the founder of ng-India, one of the world’s largest Angular communities. He lives in Gurgaon, India, and is currently writing his second book on Angular. You can reach out to him for training, evangelism and consulting opportunities.