Angular HttpClient en Interceptors in 2026: Request Handling en Sollicitatievragen

Uitgebreide handleiding voor Angular HttpClient interceptors: functionele interceptors, authenticatie, token refresh, caching en veelgestelde sollicitatievragen voor Angular-ontwikkelaars.

Angular HttpClient en Interceptors in 2026: Request Handling en Sollicitatievragen

Angular HttpClient interceptors transformeren hoe applicaties HTTP-requests verwerken, van het toevoegen van authenticatie-headers tot het implementeren van retry-logica. Angular 20 heeft functionele interceptors gestandaardiseerd als de aanbevolen aanpak, waarmee het oudere class-based patroon wordt vervangen door een meer voorspelbare en tree-shakable API.

Functionele Interceptors Zijn Standaard

Angular 20 beveelt functionele interceptors aan met withInterceptors() boven class-based interceptors. Functionele interceptors hebben een meer voorspelbare uitvoeringsvolgorde en integreren beter met standalone componenten.

HttpClient Setup met provideHttpClient

Voordat interceptors geschreven kunnen worden, moet HttpClient geconfigureerd worden in de applicatie bootstrap. Angular's standalone architectuur gebruikt provideHttpClient() in plaats van de oudere HttpClientModule.

De setup vindt plaats in de applicatieconfiguratie:

main.tstypescript
import { bootstrapApplication } from '@angular/platform-browser';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { AppComponent } from './app/app.component';
import { authInterceptor } from './interceptors/auth.interceptor';
import { loggingInterceptor } from './interceptors/logging.interceptor';

bootstrapApplication(AppComponent, {
  providers: [
    provideHttpClient(
      // Interceptors worden uitgevoerd in array-volgorde
      withInterceptors([authInterceptor, loggingInterceptor])
    ),
  ],
});

Interceptors worden uitgevoerd in de volgorde die in de array is opgegeven. De eerste interceptor verwerkt het uitgaande request eerst, en de laatste interceptor ziet de response eerst.

Anatomie van een Functionele Interceptor

Een functionele interceptor ontvangt het request en een next-functie. Het aanroepen van next(req) geeft het request door aan de volgende interceptor in de keten of aan de backend als dit de laatste interceptor is.

De basisstructuur ziet er als volgt uit:

interceptors/logging.interceptor.tstypescript
import { HttpInterceptorFn, HttpRequest, HttpHandlerFn, HttpEvent } from '@angular/common/http';
import { Observable, tap } from 'rxjs';

export const loggingInterceptor: HttpInterceptorFn = (
  req: HttpRequest<unknown>,
  next: HttpHandlerFn
): Observable<HttpEvent<unknown>> => {
  const startTime = performance.now();
  console.log(`[HTTP] ${req.method} ${req.url}`);

  return next(req).pipe(
    tap({
      next: () => {
        const duration = Math.round(performance.now() - startTime);
        console.log(`[HTTP] ${req.method} ${req.url} completed in ${duration}ms`);
      },
      error: (err) => {
        console.error(`[HTTP] ${req.method} ${req.url} failed:`, err.message);
      },
    })
  );
};

De interceptor logt de request-methode en URL, en meet vervolgens de responstijd. De tap-operator observeert de response zonder deze te wijzigen.

Authenticatie Interceptor met Token Injectie

De meeste applicaties moeten authenticatie-tokens toevoegen aan uitgaande requests. De interceptor kloont het request met de Authorization-header, aangezien HttpRequest-objecten onveranderlijk zijn.

Dit patroon haalt het token op van een service en voegt het toe:

interceptors/auth.interceptor.tstypescript
import { HttpInterceptorFn } from '@angular/common/http';
import { inject } from '@angular/core';
import { AuthService } from '../services/auth.service';

export const authInterceptor: HttpInterceptorFn = (req, next) => {
  // Injecteer services met Angular's inject()-functie
  const authService = inject(AuthService);
  const token = authService.getAccessToken();

  // Sla auth-header over voor publieke endpoints
  if (req.url.includes('/public/') || !token) {
    return next(req);
  }

  // Kloon request met Authorization-header
  const authReq = req.clone({
    setHeaders: {
      Authorization: `Bearer ${token}`,
    },
  });

  return next(authReq);
};

De inject()-functie werkt omdat interceptors worden uitgevoerd binnen Angular's injection-context. Dit elimineert de constructor-injection boilerplate van class-based interceptors.

Foutafhandeling en Retry-logica

Netwerkfouten komen voor. Een retry-interceptor kan automatisch mislukte requests opnieuw proberen met exponentiële backoff, wat de betrouwbaarheid verbetert zonder wijzigingen aan elke HTTP-aanroep te vereisen.

RxJS-operators verwerken de retry-logica:

interceptors/retry.interceptor.tstypescript
import { HttpInterceptorFn, HttpErrorResponse } from '@angular/common/http';
import { retry, timer } from 'rxjs';

export const retryInterceptor: HttpInterceptorFn = (req, next) => {
  // Alleen GET-requests opnieuw proberen (idempotent)
  if (req.method !== 'GET') {
    return next(req);
  }

  return next(req).pipe(
    retry({
      count: 3,
      delay: (error: HttpErrorResponse, retryCount: number) => {
        // Client-fouten (4xx) niet opnieuw proberen
        if (error.status >= 400 && error.status < 500) {
          throw error;
        }
        // Exponentiële backoff: 1s, 2s, 4s
        const delayMs = Math.pow(2, retryCount - 1) * 1000;
        console.log(`Retry #${retryCount} in ${delayMs}ms`);
        return timer(delayMs);
      },
    })
  );
};

De interceptor probeert alleen GET-requests opnieuw omdat POST, PUT en DELETE niet idempotent zijn. Client-fouten (4xx) slaan retries over omdat de server het request expliciet heeft afgewezen.

Klaar om je Angular gesprekken te halen?

Oefen met onze interactieve simulatoren, flashcards en technische tests.

Response Caching met HttpContext

HttpClient biedt HttpContext om metadata tussen interceptors en aanroepende code door te geven. Een caching-interceptor kan context-tokens lezen om het cache-gedrag te bepalen.

Het cache-token en de interceptor werken samen:

interceptors/cache.interceptor.tstypescript
import { HttpInterceptorFn, HttpContextToken, HttpResponse } from '@angular/common/http';
import { of, tap } from 'rxjs';

// Context-token om caching per request te controleren
export const CACHE_REQUEST = new HttpContextToken<boolean>(() => false);

const cache = new Map<string, HttpResponse<unknown>>();

export const cacheInterceptor: HttpInterceptorFn = (req, next) => {
  // Alleen cachen als expliciet aangevraagd via context
  if (!req.context.get(CACHE_REQUEST) || req.method !== 'GET') {
    return next(req);
  }

  const cacheKey = req.urlWithParams;
  const cachedResponse = cache.get(cacheKey);

  if (cachedResponse) {
    console.log(`[Cache] HIT: ${cacheKey}`);
    return of(cachedResponse.clone());
  }

  return next(req).pipe(
    tap((event) => {
      if (event instanceof HttpResponse) {
        console.log(`[Cache] STORE: ${cacheKey}`);
        cache.set(cacheKey, event.clone());
      }
    })
  );
};

De aanroepende code kiest voor caching met het context-token:

services/user.service.tstypescript
import { HttpClient, HttpContext } from '@angular/common/http';
import { CACHE_REQUEST } from '../interceptors/cache.interceptor';

export class UserService {
  constructor(private http: HttpClient) {}

  getUser(id: string) {
    return this.http.get(`/api/users/${id}`, {
      context: new HttpContext().set(CACHE_REQUEST, true),
    });
  }
}

Dit patroon houdt het caching-gedrag expliciet op de aanroepplek in plaats van verborgen in de globale configuratie.

Token Refresh met 401 Afhandeling

Wanneer access tokens verlopen, retourneert de server een 401-response. Een interceptor kan dit opvangen, het token vernieuwen en het oorspronkelijke request transparant opnieuw proberen.

De refresh-logica coordineert meerdere gelijktijdige requests:

interceptors/token-refresh.interceptor.tstypescript
import { HttpInterceptorFn, HttpErrorResponse } from '@angular/common/http';
import { inject } from '@angular/core';
import { catchError, switchMap, throwError, BehaviorSubject, filter, take } from 'rxjs';
import { AuthService } from '../services/auth.service';

let isRefreshing = false;
const refreshTokenSubject = new BehaviorSubject<string | null>(null);

export const tokenRefreshInterceptor: HttpInterceptorFn = (req, next) => {
  const authService = inject(AuthService);

  return next(req).pipe(
    catchError((error: HttpErrorResponse) => {
      if (error.status !== 401 || req.url.includes('/auth/refresh')) {
        return throwError(() => error);
      }

      if (isRefreshing) {
        // Wacht tot de refresh is voltooid
        return refreshTokenSubject.pipe(
          filter((token) => token !== null),
          take(1),
          switchMap((token) => {
            const retryReq = req.clone({
              setHeaders: { Authorization: `Bearer ${token}` },
            });
            return next(retryReq);
          })
        );
      }

      isRefreshing = true;
      refreshTokenSubject.next(null);

      return authService.refreshToken().pipe(
        switchMap((response) => {
          isRefreshing = false;
          refreshTokenSubject.next(response.accessToken);
          const retryReq = req.clone({
            setHeaders: { Authorization: `Bearer ${response.accessToken}` },
          });
          return next(retryReq);
        }),
        catchError((refreshError) => {
          isRefreshing = false;
          authService.logout();
          return throwError(() => refreshError);
        })
      );
    })
  );
};

Het BehaviorSubject voorkomt meerdere refresh-aanroepen wanneer verschillende requests tegelijkertijd falen. Zodra het token is vernieuwd, proberen alle wachtende requests opnieuw met het nieuwe token.

Sollicitatievragen: HttpClient en Interceptors

Technische sollicitatiegesprekken testen vaak het begrip van HTTP-afhandelingspatronen. Deze vragen komen regelmatig voor bij Angular-posities.

V: Waarom hebben functionele interceptors de voorkeur boven class-based interceptors?

Functionele interceptors hebben een meer voorspelbare uitvoeringsvolgorde. De Angular-documentatie stelt dat functionele interceptors met withInterceptors() een strikte array-volgorde handhaven, terwijl DI-gebaseerde interceptors volgordeproblemen kunnen hebben in complexe module-hierarchieën. Functionele interceptors produceren ook kleinere bundles omdat ze beter tree-shaken.

V: Hoe voorkom je dat een interceptor wordt uitgevoerd op specifieke requests?

Er zijn twee benaderingen. Ten eerste, controleer het URL-patroon in de interceptor en roep next(req) aan zonder wijziging voor uitgesloten paden. Ten tweede, gebruik HttpContext-tokens die de aanroepende code instelt om af te zien van interceptor-gedrag. De context-benadering houdt de uitsluitingsbeslissing op de aanroepplek.

V: Wat gebeurt er als je vergeet next() aan te roepen in een interceptor?

Het request bereikt nooit de server. Het Observable dat door next() wordt geretourneerd, vertegenwoordigt de rest van de interceptor-keten en de daadwerkelijke HTTP-aanroep. Zonder dit aan te roepen, zien volgende interceptors en de backend het request nooit. Dit is opzettelijk voor caching-interceptors die gecachte responses retourneren.

V: Hoe ga je om met gelijktijdige requests tijdens token refresh?

Gebruik een BehaviorSubject om te coordineren. Wanneer de eerste 401 een refresh triggert, stel een flag in en emit null op het subject. Volgende 401's wachten op het subject met filter totdat het nieuwe token arriveert. Dit voorkomt meerdere refresh-aanroepen en zorgt ervoor dat alle wachtende requests opnieuw proberen met hetzelfde verse token.

V: Kunnen interceptors de response body wijzigen?

Ja. Gebruik map() op het Observable dat door next() wordt geretourneerd om HttpResponse-objecten te transformeren. Aangezien responses onveranderlijk zijn, kloon ze met gewijzigde bodies. Dit patroon werkt voor het toevoegen van berekende velden, het uitpakken van API-envelopes of het normaliseren van datastructuren.

Voor meer Angular sollicitatievoorbereiding, zie de HttpClient sollicitatievragen module.

Testen van Functionele Interceptors

Interceptors vereisen testen met gemockte HTTP-handlers. Angular's HttpTestingController werkt met interceptors die zijn geregistreerd via provideHttpClient().

Een test-setup ziet er als volgt uit:

interceptors/auth.interceptor.spec.tstypescript
import { TestBed } from '@angular/core/testing';
import { HttpClient, provideHttpClient, withInterceptors } from '@angular/common/http';
import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
import { authInterceptor } from './auth.interceptor';
import { AuthService } from '../services/auth.service';

describe('authInterceptor', () => {
  let httpClient: HttpClient;
  let httpTesting: HttpTestingController;
  let authServiceSpy: jasmine.SpyObj<AuthService>;

  beforeEach(() => {
    authServiceSpy = jasmine.createSpyObj('AuthService', ['getAccessToken']);

    TestBed.configureTestingModule({
      providers: [
        provideHttpClient(withInterceptors([authInterceptor])),
        provideHttpClientTesting(),
        { provide: AuthService, useValue: authServiceSpy },
      ],
    });

    httpClient = TestBed.inject(HttpClient);
    httpTesting = TestBed.inject(HttpTestingController);
  });

  it('should add Authorization header when token exists', () => {
    authServiceSpy.getAccessToken.and.returnValue('test-token');

    httpClient.get('/api/data').subscribe();

    const req = httpTesting.expectOne('/api/data');
    expect(req.request.headers.get('Authorization')).toBe('Bearer test-token');
    req.flush({ data: 'test' });
  });

  it('should skip Authorization for public endpoints', () => {
    authServiceSpy.getAccessToken.and.returnValue('test-token');

    httpClient.get('/public/health').subscribe();

    const req = httpTesting.expectOne('/public/health');
    expect(req.request.headers.has('Authorization')).toBeFalse();
    req.flush({ status: 'ok' });
  });
});

De test verifieert zowel de header-injectie als de uitzondering voor publieke endpoints zonder daadwerkelijke netwerkverzoeken te doen.

Productiepatronen: Combineren van Interceptors

Echte applicaties stapelen meerdere interceptors. De volgorde is belangrijk: authenticatie moet voor logging worden uitgevoerd, en caching moet voor retry-logica worden uitgevoerd.

Een productieconfiguratie kan er als volgt uitzien:

main.tstypescript
import { bootstrapApplication } from '@angular/platform-browser';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { AppComponent } from './app/app.component';
import { authInterceptor } from './interceptors/auth.interceptor';
import { tokenRefreshInterceptor } from './interceptors/token-refresh.interceptor';
import { cacheInterceptor } from './interceptors/cache.interceptor';
import { retryInterceptor } from './interceptors/retry.interceptor';
import { loggingInterceptor } from './interceptors/logging.interceptor';

bootstrapApplication(AppComponent, {
  providers: [
    provideHttpClient(
      withInterceptors([
        // Volgorde: auth -> refresh -> cache -> retry -> logging
        authInterceptor,
        tokenRefreshInterceptor,
        cacheInterceptor,
        retryInterceptor,
        loggingInterceptor,
      ])
    ),
  ],
});

Deze volgorde zorgt ervoor dat tokens worden toegevoegd voor de refresh-afhandeling, cache-controles plaatsvinden voor retries, en logging de uiteindelijke timing vastlegt.

Voor bredere Angular-patronen, zie de RxJS fundamentals module die de operators behandelt die in interceptors worden gebruikt.

Begin met oefenen!

Test je kennis met onze gespreksimulatoren en technische tests.

Kernpunten voor Angular HttpClient Interceptors

  • Functionele interceptors met withInterceptors() zijn de Angular 20-standaard, met voorspelbare volgorde en betere tree-shaking dan class-based alternatieven
  • Gebruik inject() binnen interceptors om toegang te krijgen tot services zonder constructor-boilerplate
  • Kloon requests met req.clone() om headers te wijzigen aangezien HttpRequest-objecten onveranderlijk zijn
  • HttpContext-tokens maken per-request gedrag expliciet op de aanroepplek in plaats van verborgen in globale configuratie
  • Token refresh-interceptors moeten gelijktijdige requests coordineren met BehaviorSubject om meerdere refresh-aanroepen te voorkomen
  • De volgorde van interceptors is belangrijk: authenticatie voor caching, caching voor retry, logging als laatste
Dagelijkse challenge

Zie jij de bug in Angular?

Een echt codefragment, een verborgen bug, één poging per dag. Zonder account uit te proberen.

Anthony Fillion-Maillet

Geschreven door

Anthony Fillion-Maillet

Oprichter van SharpSkill

Al meer dan 10 jaar fullstack-ontwikkelaar. Hij leidt SharpSkill en staat in voor alles wat hier verschijnt.

Bijgewerkt op 22 augustus 2026

Delen

Gerelateerde artikelen