DEV Community

Cover image for 🧱 Lesson 13B: Centralized Error Handling & Validation (Frontend)
Farrukh Rehman
Farrukh Rehman

Posted on

🧱 Lesson 13B: Centralized Error Handling & Validation (Frontend)

Series: From Code to Cloud: Building a Production-Ready .NET Application
By: Farrukh Rehman - Senior .NET Full Stack Developer / Team Lead
LinkedIn: https://linkedin.com/in/farrukh-rehman
GitHub: https://github.com/farrukh1212cs

Source Code Backend : https://github.com/farrukh1212cs/ECommerce-Backend.git

Source Code Frontend : https://github.com/farrukh1212cs/ECommerce-Frontend.git

🎯 Introduction
In the previous lesson (13A), we standardized the backend error responses. Now, in Lesson 13B, we will implement the Frontend counterpart using Angular Interceptors.

Instead of handling .subscribe({ error: ... }) in every single component or service, we will use a Global HTTP Interceptor. This interceptor will catch all error responses from the API, parse the standardized JSON, and display a user-friendly notification (using PrimeNG Toasts).

Step 1: Define the Error Response Interface

We need a TypeScript interface that matches the C# ErrorResponse class we created in the backend. This ensures type safety when parsing errors.

File: src/app/core/interceptors/error.interceptor.ts

export interface ErrorResponse {
    statusCode: number;
    message: string;
    details?: any;
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Implement the Error Interceptor

The interceptor sits in the HTTP pipeline. It checks if an error occurs, parses the message from the backend, and uses MessageService to show a toast.

File: src/app/core/interceptors/error.interceptor.ts

import { HttpErrorResponse, HttpInterceptorFn } from '@angular/common/http';
import { inject } from '@angular/core';
import { MessageService } from 'primeng/api';
import { catchError, throwError } from 'rxjs';
export const errorInterceptor: HttpInterceptorFn = (req, next) => {
    const messageService = inject(MessageService);
    return next(req).pipe(
        catchError((error: HttpErrorResponse) => {
            let errorMessage = 'An unexpected error occurred';
            let summary = 'Error';

            // 1. Handle Network Errors (Server offline / No internet)
            if (error.status === 0) {
                errorMessage = 'Network error. Please check your connection.';
                summary = 'Network Error';
            }
            // 2. Handle Backend Standardized Errors
            else if (error.error && typeof error.error === 'object' && 'message' in error.error) {
                const backendError = error.error as ErrorResponse;
                errorMessage = backendError.message || errorMessage;

                // Optional: Log validation details if present
                if (backendError.statusCode === 400 && backendError.details) {
                     console.error('Validation Details:', backendError.details);
                }
            } 
            // 3. Fallback for specific status codes
            else if (error.status === 404) {
                errorMessage = 'Resource not found';
            } else if (error.status === 401) {
                errorMessage = 'Unauthorized access';
                summary = 'Unauthorized';
            } else if (error.status === 403) {
                errorMessage = 'Forbidden access';
                summary = 'Forbidden';
            } else if (error.status === 500) {
                errorMessage = 'Internal Server Error';
                summary = 'Server Error';
            }
            // 4. Display Toast Notification
            messageService.add({ 
                severity: 'error', 
                summary: summary, 
                detail: errorMessage,
                life: 5000 
            });
            // Re-throw the error so specific components can still handle it if needed
            return throwError(() => error);
        })
    );
};
Enter fullscreen mode Exit fullscreen mode

Step 3: Register the Interceptor
For the interceptor to work, it must be registered in the application configuration.

File: src/app.config.ts

import { errorInterceptor } from './core/interceptors/error.interceptor';
// ... other imports
export const appConfig: ApplicationConfig = {
    providers: [
        // ...
        provideHttpClient(
            withFetch(), 
            withInterceptors([
                authInterceptor, 
                errorInterceptor // <--- Add this here
            ])
        ),
        // ...
    ]
};
Enter fullscreen mode Exit fullscreen mode

Step 4: Verification

To verify the implementation:

  1. Run the application (ng serve).
  2. Trigger an error (e.g., attempt to login with invalid credentials).
  3. Expected Result: A red toast notification appears at the top right with the exact error message returned by the backend (e.g., "Invalid credentials" or "Resource not found").

This completes the Centralized Error Handling cycle. Both Backend and Frontend are now synchronized over a standard error protocol.

Next Lecture Preview
Lecture 14 : Cloud Deployment (Azure / AWS)

Deploying backend, frontend, and database to the cloud with managed infrastructure services.

Top comments (0)