Skip to main content

HttpInterceptor

Interceptors sit between the client and the FetchBackend. Each interceptor receives the request context and a next function, and must return an Observable<HttpEvent>. Interceptors are applied in the order they are registered and can inspect or modify the request before calling next, and inspect or transform the response stream after.


HttpInterceptor interface

Class-based interceptor. Implement intercept and pass an instance in FetchBackend.Options.interceptors.

interface HttpInterceptor<TRequest = HttpBackend.RequestInit> {
intercept(req: TRequest, next: HttpHandler<TRequest>): Observable<HttpEvent>;
}
import { HttpInterceptor, HttpHandler, HttpBackend } from '@opra/client';
import { Observable } from 'rxjs';

export class AuthInterceptor implements HttpInterceptor {
constructor(private readonly getToken: () => string) {}

intercept(req: HttpBackend.RequestInit, next: HttpHandler): Observable<HttpEvent> {
req.headers.set('Authorization', `Bearer ${this.getToken()}`);
return next.handle(req);
}
}

HttpInterceptorFn type

Functional interceptor — a plain function with the same signature. Simpler to write when no class state is needed.

type HttpInterceptorFn<TRequest = HttpBackend.RequestInit> = (
req: TRequest,
next: HttpHandlerFn<TRequest>,
) => Observable<HttpEvent<unknown>>;
import { HttpInterceptorFn } from '@opra/client';

export const loggingInterceptor: HttpInterceptorFn = (req, next) => {
console.log('→', req.method, req.url);
return next(req);
};

HttpHandler / HttpHandlerFn

The next argument passed to class-based interceptors is an HttpHandler:

interface HttpHandler<TRequest = HttpBackend.RequestInit> {
handle(req: TRequest): Observable<HttpEvent>;
}

The next argument passed to functional interceptors is an HttpHandlerFn:

type HttpHandlerFn<TRequest = HttpBackend.RequestInit> =
(req: TRequest) => Observable<HttpEvent>;

Registering interceptors

Pass interceptors in FetchBackend.Options when constructing the client. Duplicates are de-duped automatically.

const client = new OpraHttpClient('https://api.example.com', {
interceptors: [
new AuthInterceptor(() => localStorage.getItem('token') ?? ''),
loggingInterceptor,
],
});

Examples

Add auth token

export class AuthInterceptor implements HttpInterceptor {
intercept(req: HttpBackend.RequestInit, next: HttpHandler): Observable<HttpEvent> {
req.headers.set('Authorization', `Bearer ${getAccessToken()}`);
return next.handle(req);
}
}

Retry on network errors

import { retry } from 'rxjs';

export const retryInterceptor: HttpInterceptorFn = (req, next) =>
next(req).pipe(retry({ count: 2, delay: 300 }));

Add correlation ID

import { tap } from 'rxjs';
import { HttpEventType } from '@opra/client';

export const correlationInterceptor: HttpInterceptorFn = (req, next) => {
req.headers.set('X-Correlation-Id', crypto.randomUUID());
return next(req).pipe(
tap(event => {
if (event.type === HttpEventType.Response) {
console.log('←', event.response.status);
}
}),
);
};