How Do You Handle API Errors in Angular?
1. What is API Error Handling in Angular?
API error handling means detecting and managing errors that occur when an Angular application communicates with a backend API.
For example, your Angular application may call an ASP.NET Core Web API:
Angular
|
| HTTP Request
v
ASP.NET Core Web API
|
| HTTP Response
v
Angular
The API may return errors such as:
| Status | Meaning |
|---|---|
400 |
Bad Request / validation error |
401 |
Unauthorized |
403 |
Forbidden |
404 |
Resource not found |
409 |
Conflict |
500 |
Internal Server Error |
503 |
Service unavailable |
0 |
Often network/CORS/connectivity failure from Angular's perspective |
Angular's HttpClient returns an Observable, so API errors are commonly handled using RxJS catchError().
2. Full Simple Sample Program
Let's use a simple Angular application that calls:
GET https://localhost:7001/api/products
We will handle errors inside a reusable ProductService.
product.ts
export interface Product {
id: number;
name: string;
price: number;
}
product.service.ts
import { inject, Injectable } from '@angular/core';
import {
HttpClient,
HttpErrorResponse
} from '@angular/common/http';
import {
catchError,
Observable,
throwError
} from 'rxjs';
import { Product } from './product';
@Injectable({
providedIn: 'root'
})
export class ProductService {
private http = inject(HttpClient);
private apiUrl = 'https://localhost:7001/api/products';
getProducts(): Observable<Product[]> {
return this.http.get<Product[]>(this.apiUrl)
.pipe(
catchError(error => this.handleError(error))
);
}
private handleError(error: HttpErrorResponse) {
let message = 'An unexpected error occurred.';
if (error.status === 0) {
message = 'Unable to connect to the server.';
}
else if (error.status === 400) {
message = 'Invalid request.';
}
else if (error.status === 401) {
message = 'You are not authorized.';
}
else if (error.status === 403) {
message = 'You do not have permission.';
}
else if (error.status === 404) {
message = 'Requested resource was not found.';
}
else if (error.status >= 500) {
message = 'Server error. Please try again later.';
}
console.error('API Error:', error);
return throwError(() => new Error(message));
}
}
products.ts
import { Component, inject } from '@angular/core';
import { ProductService } from './product.service';
import { Product } from './product';
@Component({
selector: 'app-products',
standalone: true,
templateUrl: './products.html'
})
export class Products {
private productService = inject(ProductService);
products: Product[] = [];
errorMessage = '';
loadProducts() {
this.errorMessage = '';
this.productService.getProducts()
.subscribe({
next: (data) => {
this.products = data;
},
error: (error) => {
this.errorMessage = error.message;
}
});
}
}
products.html
<h2>Products</h2>
<button (click)="loadProducts()">
Load Products
</button>
@if (errorMessage) {
<p>
{{ errorMessage }}
</p>
}
@for (product of products; track product.id) {
<div>
{{ product.name }} - {{ product.price }}
</div>
}
app.config.ts
For a standalone Angular application, configure HttpClient:
import {
ApplicationConfig
} from '@angular/core';
import {
provideHttpClient
} from '@angular/common/http';
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient()
]
};
3. Explanation
The important part is:
return this.http.get<Product[]>(this.apiUrl)
.pipe(
catchError(error => this.handleError(error))
);
The flow is:
Component
|
| getProducts()
v
ProductService
|
| HttpClient GET
v
ASP.NET Core API
|
+------------------------+
| |
Success Error
| |
v v
next() catchError()
| |
v v
Display products handleError()
|
v
throwError()
|
v
Component error()
HttpErrorResponse
Angular represents HTTP failures using HttpErrorResponse.
private handleError(error: HttpErrorResponse)
It contains useful information such as:
error.status
error.statusText
error.message
error.error
error.url
For example:
if (error.status === 404) {
message = 'Requested resource was not found.';
}
4. What does catchError() do?
catchError() is an RxJS operator that intercepts an error emitted by an Observable.
this.http.get<Product[]>(url)
.pipe(
catchError(error => ...)
);
Without catchError(), the error can still reach the subscriber:
.subscribe({
next: data => {},
error: error => {}
});
But catchError() lets you process or transform the error before passing it to the component.
5. What does throwError() do?
After processing the API error, we can return an error Observable:
return throwError(() => new Error(message));
For example, the API returns:
HTTP 404
The service converts it into:
Requested resource was not found.
Then the component receives it:
error: (error) => {
this.errorMessage = error.message;
}
6. Service-Level vs Component-Level Error Handling
There are generally several places where errors can be handled.
Component
this.productService.getProducts()
.subscribe({
next: data => {
this.products = data;
},
error: error => {
this.errorMessage = error.message;
}
});
Useful for UI-specific behavior.
For example:
Show "Products could not be loaded"
Service
.pipe(
catchError(error => this.handleError(error))
)
Useful when error handling is specific to that service or API operation.
HTTP Interceptor
For larger applications, common/global errors are usually handled using an HTTP interceptor.
For example:
401 → redirect to login
403 → show forbidden message
500 → show generic server error
Logging → log all HTTP errors
Architecture:
Component
|
v
ProductService
|
v
Error Interceptor
|
v
ASP.NET Core Web API
7. Global Error Handling Using an Interceptor
In a real Angular application, you often don't want to repeat:
catchError(...)
in every service.
You can create an interceptor.
error.interceptor.ts
import { HttpErrorResponse, HttpInterceptorFn }
from '@angular/common/http';
import { catchError, throwError } from 'rxjs';
export const errorInterceptor: HttpInterceptorFn =
(req, next) => {
return next(req).pipe(
catchError((error: HttpErrorResponse) => {
if (error.status === 401) {
console.error('Unauthorized');
}
else if (error.status === 403) {
console.error('Forbidden');
}
else if (error.status === 404) {
console.error('Resource not found');
}
else if (error.status >= 500) {
console.error('Server error');
}
return throwError(() => error);
})
);
};
Register it in app.config.ts
import {
ApplicationConfig
} from '@angular/core';
import {
provideHttpClient,
withInterceptors
} from '@angular/common/http';
import {
errorInterceptor
} from './error.interceptor';
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient(
withInterceptors([
errorInterceptor
])
)
]
};
Now the interceptor can inspect errors from requests passing through that HttpClient configuration.
8. Which Approach Should You Use?
A practical Angular application commonly combines them:
API Error
|
v
Error Interceptor
|
Common/global handling
|
v
Service
|
API-specific handling
|
v
Component
|
UI-specific handling
For example:
Interceptor
401 → authentication handling
403 → authorization handling
500 → global notification/logging
Service
404 → Product not found
409 → Product already exists
Component
Display the appropriate message
9. Important: Don't Expose Raw Server Errors
Avoid directly displaying:
error.message
or:
error.error
to the user without considering what the backend returns.
A backend might expose technical information that should not appear in the UI.
Prefer converting backend errors into user-friendly messages:
if (error.status >= 500) {
message = 'Something went wrong. Please try again later.';
}
Technical details can instead be logged appropriately.
Key Points
- Angular uses
HttpClientto communicate with APIs. HttpClientreturns Observables.- HTTP failures are represented by
HttpErrorResponse. - Use RxJS
catchError()to intercept Observable errors. - Use
throwError()when you want to propagate an error. - Handle UI-specific errors in the component.
- Handle API/service-specific errors in the service when appropriate.
- Use an HTTP interceptor for common/global HTTP error handling.
401means unauthenticated/unauthorized authentication-wise;403means authenticated but not permitted in the typical API authorization model.404means the requested resource was not found.5xxindicates a server-side failure.status === 0commonly means Angular did not receive a normal HTTP response; causes can include network failure, CORS blocking, or the server being unreachable.- Don't expose sensitive backend exception details to users.
- In modern standalone Angular, functional interceptors with
withInterceptors()are a clean approach.
Interview Questions and Answers
1. How do you handle API errors in Angular?
Angular API errors can be handled using HttpErrorResponse, RxJS catchError(), and the error callback of subscribe(). For application-wide HTTP error handling, an HTTP interceptor is commonly used.
2. What is HttpErrorResponse?
HttpErrorResponse represents an error returned or encountered during an Angular HTTP request.
catchError((error: HttpErrorResponse) => {
console.log(error.status);
return throwError(() => error);
})
3. What is catchError()?
catchError() is an RxJS operator used to intercept and handle errors emitted by an Observable.
.pipe(
catchError(error => {
return throwError(() => error);
})
)
4. What is throwError()?
throwError() creates an Observable that emits an error.
return throwError(
() => new Error('API failed')
);
It is commonly used to propagate an error after processing it.
5. Where should API errors be handled?
It depends on the scope:
Component → UI-specific handling
Service → API/business-specific handling
Interceptor → Global/common HTTP handling
6. Why use an HTTP interceptor for error handling?
An interceptor centralizes common error handling so the same logic does not have to be duplicated across every service.
For example:
401 → authentication handling
403 → authorization handling
500 → generic server-error handling
7. How do you handle a 401 error?
A global error interceptor can detect it:
if (error.status === 401) {
// Clear invalid authentication state
// Redirect to login if appropriate
}
The backend must still enforce authentication and authorization; Angular-side handling is primarily for user experience.
8. What is the difference between 401 and 403?
401 → Authentication is missing/invalid
403 → Request is understood, but access is forbidden
A common Angular UX is:
401 → Login
403 → Unauthorized/Access Denied page
9. What does status === 0 mean in Angular?
It means Angular did not receive a normal HTTP response. Common causes include:
API unavailable
Network failure
CORS blocking
Connection failure
You need to inspect the browser's Network tab and console to determine the actual cause.
Interview Quick Answer
How do you handle API errors in Angular?
Angular API errors are typically handled using HttpErrorResponse and RxJS catchError(). For application-wide handling, I use an HTTP interceptor for common cases such as 401, 403, and 500, while service/component-level handling is used for operation-specific errors and user-facing messages.