DEV Community
In Angular 19.2.7, a bug fix was made to set the appropriate status code and headers when a request fails in httpResource
. The response has the same metadata in successful and failed HTTP requests; therefore, status code management can be handled similarly in all paths.
When the status code is 200, the application will show a successful message. When the status code is non-200, the application shows status code specific error messages.
The Problem It Solves
Before the fix, httpResource
did not expose the status code and headers for failed HTTP requests. Developers could only check the error and status signals to gather the error information for investigation. In the latest Angular 19, httpResource
sets these values in the response properly, allowing developers to inspect them to facilitate investigation, resolve the error and construct a user-friendly error message.
First, let's update Angular to the latest version. As of this writing, the Angular version is 19.2.7.
ng update @angular/core @angular/cli
Install zod to parse the response of the httpResource
npm i zod
In this demo, we will create a component to retrieve Pokemon's physical attributes using httpResource
. When the Pokemon is valid, httpResource
returns a JSON object and status code 200. When the request cannot search the Pokemon, the response's status code is 404 and the response error is 'Http failure response for <URL>: 404 OK'.
Defining the Pokemon Schema
First, we define a Pokemon schema to parse the HTTP response of the httpResource
:
import { z } from 'zod';
export const PokemonSchema = z.object({
id: z.number(),
name: z.string(),
weight: z.number(),
height: z.number(),
});
export type Pokemon = z.infer<typeof PokemonSchema>;
A valid Pokemon object should have an id, name, weight and height.
Utility Function
Here's a utility function to obtain the httpResource's error, status code and headers:
export function makeResourceRefStatus(resourceRef: HttpResourceRef<Pokemon | undefined>){
return {
error: computed(() =>
resourceRef.error() ? resourceRef.error() as HttpErrorResponse : undefined
),
statusCode: computed(() => resourceRef.statusCode() ? resourceRef.statusCode() : undefined),
headers: computed(() => resourceRef.headers() ? resourceRef.headers() : undefined)
value: computed(() => resourceRef.hasValue() ? resourceRef.value() : undefined)
}
}
The makeResourceRefStatus
function accepts an HttpResourceRef
and returns the following properties in an Object:
- error: the HTTP Response error thrown by the HTTP request
- statusCode: the status code of the HTTP response. It is available when response returns an error.
- headers: the HTTP response headers. The headers are available when response returns an error.
- value: a Pokemon object or undefined
Retrieving Pikachu with httpResource
Here's how we use these components:
@Component({
selector: 'app-resource-pokemon',
templateUrl: './httpresource-pokemon.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class HttpResourcePokemonComponent {
url = signal('');
prev = signal('');
// Swap the Pokemon name between Pikachu and Angular
setUrl() {
const base = 'https://pokeapi.co/api/v2/pokemon';
const pikachuUrl = `${base}/pikachu`;
const angularUrl = `${base}/angular`;
this.prev.set(this.url());
if (!this.url()) {
this.url.set(pikachuUrl);
} else {
this.name.set(this.name() === pikachuUrl ? angularUrl : pikachuUrl);
}
}
// Make backend request to Pokemon API when the reactive URL is updated
pokemonResourceRef = httpResource(
() => this.url() !== this.prev() ? this.url() : undefined,
{
parse: PokemonSchema.parse,
}
);
resourceRefStatus = makeResourceRefStatus(this.pokemonResourceRef);
// HTTP response error
error = this.resourceRefStatus.error;
// HTTP response status code
statusCode = this.resourceRefStatus.statusCode;
// HTTP response headers
headers = this.resourceRefStatus.headers;
// JSON object
value = this.resourceRefStatus.value;
}
When I clicked the button, the url signal swaps value from the pikachu URL to the angular URL and vice versa. The reactive function constructs a new URL and makes backend requests to the Pokemon API to retrieve the Pokemon. The makeResourceRefStatus
utility function uses the this.pokemonResourceRef
to create error, status code, headers and value computed signals. The HTML template displays their values for successful and failure requests.
<div>
<button (click)="setUrl()">
<ng-content>Show Http Status Code</ng-content>
</button>
<p>{{ `Url: ${url()}` }}</p>
<p>Status Code: {{ statusCode() }}</p>
@if (error()) {
<p>Error: {{ error()?.message }}</p>
} @else if (value()) {
<p>
Pikachu: ID: {{ value()?.id }}, Name: {{ value()?.name }}, Weight:
{{ value()?.weight }}, Height: {{ value()?.height }}
</p>
} @if (this.headers()) {
<p style="text-decoration: underline;">Headers</p>
@let keys = headers()?.keys() || [];
<ol>
@for (key of keys; track key) {
<li>{{ `headers["${key}"] = ${headers()?.get(key)}` }}</li>
}
</ol>
}
</div>
When the request fails, the error
computed signal is defined, and the template displays the HTTP response error message. When the request is successful, the value
computed signal is a JSON object. The template shows Pikachu's ID, name, weight, and height. In either case, the response provides the status code and headers. Since "angular" is not a Pokemon, the status code of the failed request is 404 and the content type is "text/plain; charset=utf-8".
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
imports: [HttpResourcePokemonComponent],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class App {}
<div>
<app-resource-pokemon />
<hr />
</div>
In this code, the App
component serves as the root component of the Angular application. Its primary responsibility is to integrate and render the HttpResourcePokemonComponent
.
Let's break down the App component:
- Imports: The App component imports HttpResourcePokemonComponent.
- Template: The template of the App component defines the structure of what will be rendered in the application's main view.
- It includes an instance of HttpResourcePokemonComponent.
- When the button is clicked the first time, the httpResource retrieves Pikachu, displays the status code, headers and the JSON object.
- When the button click occurs again, the httpResource retrieves Angular which is a phantom Pokemon. Therefore, the request failed and displayed 404 status code.
- app-root Selector: The App component is typically associated with the selector in the index.html file. This means that when the Angular application is loaded, the App component is the first component to be rendered in the browser.
Benefits
- Storing the status code and headers in an errored HTTP request offers several advantages:
- Convey better information: The status code conveys useful information of HTPP request. For example, the 404 status implies the Angular is an invalid input and a fake Pokemon.
- Friendly error message: Application can provide descriptive error message to users based on the status code. In this demo, the status code is 404 and a friendly error message can be shown instead of “Http failure response for <URL>: 404 OK”.
- Centralized Logic: The application can define centralized logic for handling user messages using the status code and custom headers. They can be combined to create common error messages to be shown in the user interfaces.
Conclusion
Successful and failed httpResource
requests include status code and headers in HTTP responses. Failed requests can use the status code to convey better information and construct friendly error messages. The application can define centralized logic by using the status code and/or custom header to create common user messages to be displayed in user interfaces.
Resources
- The PR relevant to httpResource - https://github.com/angular/angular/pull/60802
- The HttpResource documentation: https://angular.dev/api/common/http/httpResource
- The HttpResourceRef documentation: https://angular.dev/api/common/http/HttpResourceRef
- Stackblitz Demo: https://stackblitz.com/edit/stackblitz-starters-qaaygtyb?file=src%2Fhttp%2Fhttpresource-pokemon.component.ts
For further actions, you may consider blocking this person and/or reporting abuse
Top comments (0)