Best way API handling in angular

Roshan Mali
3 min readNov 25, 2023

--

Directory Structure

Below codebase typical Angular project structure for organizing services and interceptors:

This structure keeps core services and interceptors separate from module-specific ones

/src
/app
/core
/services
- auth.service.ts
- error-handler.service.ts
/interceptors
- auth.interceptor.ts
- error.interceptor.ts
/modules
/module-name
/services
- module-specific.service.ts

Service for API Calls

For each module or functionality, we can create a dedicated service. For example, module-specific.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';

@Injectable({
providedIn: 'root'
})
export class ModuleSpecificService {

constructor(private http: HttpClient) {}

getSomeData(): Observable<any> {
return this.http.get('/api/data').pipe(
catchError(error => {
// Handle or transform error before rethrowing
return throwError(error);
})
);
}
}

Error Handling Service (error-handler.service.ts)

This service will handle different HTTP errors based on their status codes:

import { Injectable } from '@angular/core';
import { HttpErrorResponse } from '@angular/common/http';

@Injectable({
providedIn: 'root'
})
export class ErrorHandlerService {

public handleError(error: HttpErrorResponse): void {
if (error.error instanceof ErrorEvent) {
// Client-side or network error
console.error('An error occurred:', error.error.message);
} else {
// Backend returned an unsuccessful response code
console.error(`Backend returned code ${error.status}, body was: `, error.error);
this.handleServerError(error);
}
}

private handleServerError(error: HttpErrorResponse): void {
switch (error.status) {
case 401:
// Handle unauthorized error
break;
case 403:
// Handle forbidden error
break;
case 404:
// Handle not found error
break;
case 500:
// Handle internal server error
break;
// Add more cases as needed
default:
// Handle other errors
break;
}
}
}

Error Interceptor (error.interceptor.ts)

The error interceptor will use the ErrorHandlerService to handle errors:

import { Injectable } from '@angular/core';
import {
HttpInterceptor,
HttpRequest,
HttpHandler,
HttpErrorResponse
} from '@angular/common/http';
import { catchError } from 'rxjs/operators';
import { throwError } from 'rxjs';
import { ErrorHandlerService } from '../services/error-handler.service';

@Injectable()
export class ErrorInterceptor implements HttpInterceptor {

constructor(private errorHandlerService: ErrorHandlerService) {}

intercept(request: HttpRequest<any>, next: HttpHandler) {
return next.handle(request).pipe(
catchError((error: HttpErrorResponse) => {
this.errorHandlerService.handleError(error);
return throwError(error);
})
);
}
}

Implementing the Interceptors

Register the interceptors in app.module.ts:

import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { AuthInterceptor } from './core/interceptors/auth.interceptor';
import { ErrorInterceptor } from './core/interceptors/error.interceptor';

@NgModule({
// ...
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true }
],
// ...
})
export class AppModule { }

Best Practices we can follow

Modular Services: Keeping services modular and specific to features or modules helps in maintaining a clean codebase and ensures services are easily testable.

Global Error Handling: Using a centralized error handling service ensures consistency in how errors are processed and logged.

Interceptors for Cross-Cutting Concerns: Using interceptors for tasks like adding authentication tokens and handling errors globally helps to keep your service methods clean and focused on their primary responsibilities.

Security Handling: The authentication interceptor adds security tokens to every HTTP request, ensuring secure API communication. Always be cautious with handling tokens and sensitive data.

Error Interceptor: This provides a centralized place to handle all HTTP errors. For example, you can redirect to a login page on a 401 (Unauthorized) error, or show a toast message for a 500 (Server Error).

Implementing these practices leads to a more maintainable, secure, and scalable Angular application.

--

--