Angular Interceptors: Making HTTP Requests Awesome

Supercharge Your HTTP Requests with Angular Interceptors

Prince Singh
5 min readAug 24, 2024
Angular Interceptors Working

Ever heard of a middleman who’s always working behind the scenes, making sure everything flows smoothly?

Well, in the world of Angular (a popular front-end web framework), there’s something like that, and it’s called an Interceptor.

If you’re not familiar with Angular, don’t worry — by the end of this article, you’ll see how cool and useful these interceptors are, even if you’re working in another programming environment!

What Is an Angular Interceptor, Anyway?

Imagine you’re sending a letter (a.k.a. an HTTP request) to your friend (the server). Before your letter reaches the post office (the network), someone steps in and adds a cool sticker (like an authentication token) to it. That someone is the interceptor.

Now, when the letter (HTTP request) reaches your friend (the server), they instantly know it’s from you because of the sticker!

But it doesn’t stop there. When your friend writes back to you (the HTTP response), the interceptor can sneak in again and check the reply, maybe log it, or modify it before you read it.

It’s like having a super helpful, invisible assistant that ensures everything goes according to plan.

In Simple Terms

An Angular interceptor is a piece of code that intercepts HTTP requests before they’re sent to the server and responses before they’re handed over to your application. This interception allows you to modify, log, handle errors, or perform any action on the data flowing between your app and the server.

Why Do We Need Interceptors?

Instead of sprinkling the same code across various components and services, interceptors centralize common tasks. This not only keeps your code DRY (Don’t Repeat Yourself) but also enhances maintainability and scalability.

Why Should You Care?

Even if you’re not using Angular, the concept of intercepting requests and responses is powerful in many programming scenarios. For example:

  • Authentication:Automatically add tokens to every request so that the server knows who you are.
  • Error Handling: Catch errors globally and show friendly messages to users.
  • Logging: Record every request and response for debugging or analytics purposes.
  • Caching: Store responses temporarily so that you don’t have to make the same request twice.
  • Showing Loader or Progress: Store responses temporarily so that you don’t have to make the same request twice.
  • Redirecting Requests: Imagine rerouting a misaddressed path to its correct destination or modifying the correct one.

Let’s Dive into Some Examples!

Here are some common things you can do with Angular Interceptors.

1. Adding an Authorization Token

When your app communicates with a server that requires you to be logged in, it usually expects a token. With an interceptor, you can automatically attach this token to every outgoing request.

import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const token = localStorage.getItem('authToken');
const clonedReq = req.clone({
headers: req.headers.set('Authorization', `Bearer ${token}`)
});
return next.handle(clonedReq);
}
}

Why It’s Cool: You don’t have to remember to add the token every time you make a request — this interceptor does it for you!

2. Global Error Handling

Let’s say something goes wrong with a request. Instead of handling errors in every single place you make a request, an interceptor can catch all errors globally.

import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpErrorResponse } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';

@Injectable()
export class ErrorInterceptor implements HttpInterceptor {

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<any> {
return next.handle(req).pipe(
catchError((error: HttpErrorResponse) => {
let errorMessage = '';
if (error.error instanceof ErrorEvent) {
// Client-side error
errorMessage = `Error: ${error.error.message}`;
} else {
// Server-side error
errorMessage = `Error Code: ${error.status}\nMessage: ${error.message}`;
}
console.log(errorMessage);
return throwError(errorMessage);
})
);
}
}

Why It’s Cool: Centralized error handling makes your code cleaner and reduces redundancy. Plus, users get a consistent experience when things go wrong.

3. Logging Requests and Responses

Want to see what’s going in and out of your app? Interceptors are perfect for logging.

import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';

@Injectable()
export class LoggingInterceptor implements HttpInterceptor {

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
console.log('Request:', req);
return next.handle(req).pipe(
tap(event => {
console.log('Response:', event);
})
);
}
}

Why It’s Cool: Debugging becomes a breeze when you can see the exact requests and responses flying through your app.

4. Caching Responses

If your app is repeatedly making the same requests, you might want to cache some of those responses to save on network calls.

import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpResponse } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { tap } from 'rxjs/operators';

@Injectable()
export class CachingInterceptor implements HttpInterceptor {
private cache = new Map<string, any>();

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<any> {
if (req.method !== 'GET') {
return next.handle(req);
}
const cachedResponse = this.cache.get(req.urlWithParams);
if (cachedResponse) {
return of(cachedResponse);
}
return next.handle(req).pipe(
tap(event => {
if (event instanceof HttpResponse) {
this.cache.set(req.urlWithParams, event);
}
})
);
}
}

Why It’s Cool: Caching speeds up your app by reducing the number of requests it needs to make to the server, saving time and bandwidth.

Not Just Angular: The Interceptor Pattern Everywhere

While we’re spotlighting Angular, the interceptor (or middleware) pattern is ubiquitous in software development.

From Express.js middleware in Node.jsto HTTP proxies and network firewlls, the concept of intercepting and processing requests and responses is a common thread that ensures flexibility, security, and efficiency across various platforms.

Wrapping It Up

Angular interceptors might not be the most glamorous part of web development, but they’re indispensable. By handling cross-cutting concerns like authentication, logging, error handling, and more, interceptors keep your application code clean and focused on its primary responsibilities.

Even if you’re not using Angular, the concept of intercepting requests and responses is widely applicable. By integrating similar mechanisms into your own projects, you can improve performance, security, and maintainability — giving you more time to focus on building cool features!

So the next time you’re working on a web app and find yourself repeatedly writing the same code for handling HTTP requests, think about how an interceptor (or its equivalent) could make your life easier.

Keep coding, keep learning- until next time! 🎉

As always, if you’ve enjoyed this article — please feel free to leave a clap, and if you have any questions or comments feel free to leave those as well. Thanks for reading!

LinkedIn — isinghprince (for more web dev chats)

--

--

Prince Singh

I specialize in architecting enterprise-scale web applications and intuitive data visualizations from zero. Expertise - TypeScript, Rest APIs, Postgres DB