Interceptors
Interceptors wrap every request with cross-cutting logic — authentication, retry, logging, or correlation IDs — without modifying individual call sites. They run in the order they are registered and can inspect or modify both the outgoing request and the incoming response stream.
@opra/client interceptors
Register interceptors in FetchBackend.Options when constructing the client.
Class-based
Implement HttpInterceptor and pass an instance:
import { HttpInterceptor, HttpHandler, HttpBackend, OpraHttpClient } from '@opra/client';
import { Observable } from 'rxjs';
class AuthInterceptor implements HttpInterceptor {
intercept(req: HttpBackend.RequestInit, next: HttpHandler): Observable<HttpEvent> {
req.headers.set('Authorization', `Bearer ${getAccessToken()}`);
return next.handle(req);
}
}
const client = new OpraHttpClient('https://api.example.com', {
interceptors: [new AuthInterceptor()],
});
Functional
Use HttpInterceptorFn for stateless interceptors:
import { HttpInterceptorFn, OpraHttpClient } from '@opra/client';
const loggingInterceptor: HttpInterceptorFn = (req, next) => {
console.log('→', req.method, req.url);
return next(req);
};
const client = new OpraHttpClient('https://api.example.com', {
interceptors: [loggingInterceptor],
});
@opra/angular interceptors
In Angular applications, you have two options: OPRA-level interceptors (registered on the client) and Angular-level interceptors (registered as HTTP_INTERCEPTORS). Both work — choose based on scope.
OPRA-level (client-specific)
Registered on OpraClientModule and applied only to OPRA requests:
OpraClientModule.registerClient({
serviceUrl: 'https://api.example.com',
token: API_CLIENT,
interceptors: [new AuthInterceptor()],
})
Angular-level (all HTTP requests)
Registered as Angular HTTP_INTERCEPTORS and applied to every HttpClient request in the app, including OPRA requests:
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
]
Use Angular-level interceptors when the logic should apply globally (e.g. attaching auth tokens, handling 401 redirects). Use OPRA-level interceptors when the logic is specific to a particular API client.
Common patterns
Auth token
class AuthInterceptor implements HttpInterceptor {
constructor(private readonly getToken: () => string) {}
intercept(req: HttpBackend.RequestInit, next: HttpHandler): Observable<HttpEvent> {
const token = this.getToken();
if (token) req.headers.set('Authorization', `Bearer ${token}`);
return next.handle(req);
}
}
Retry on transient errors
import { retry } from 'rxjs';
const retryInterceptor: HttpInterceptorFn = (req, next) =>
next(req).pipe(retry({ count: 2, delay: 300 }));
Request logging
import { tap } from 'rxjs';
import { HttpEventType } from '@opra/client';
const loggingInterceptor: HttpInterceptorFn = (req, next) => {
const start = Date.now();
return next(req).pipe(
tap(event => {
if (event.type === HttpEventType.Response) {
console.log(`${req.method} ${req.url} — ${event.response.status} (${Date.now() - start}ms)`);
}
}),
);
};
Correlation ID
const correlationInterceptor: HttpInterceptorFn = (req, next) => {
req.headers.set('X-Correlation-Id', crypto.randomUUID());
return next(req);
};