angularpipeangular-http-interceptorsrxjs-observables

Angular 15, .pipe .subscription response modifications


  let data = { ticketId: id };
    this.apiService
      .call("tickets", "/detail", "GET", true, data, null)
      .pipe(first())
      .subscribe(
        (data) => {
          data  = this.decryptionService.decryptData(data);

          this.ticketDetail = data.data;

After submitting http request using above API and service(apiService) we will get response in data . after getting data we will use another service(decryptionService) to decrypt the response. After decryption we can use decrypted data as per our need. This functionality is working properly in implemented components but we have to implement this in whole project & I don't want to update every component manually . Is there any solution to implement decryption service globally in whole project using interceptor or using modification in apiservice, observables .pipe(first()).subscribe ??

apiService Code

export class ApiService {
    
    call = (
        service: string,
        endpoint: string,
        method: "GET" | "POST" | "PUT" | "DELETE",
        authenticated: boolean = false,
        queryOptions?: any,
        body?: any,
        options?: any,
        isFormData?: boolean
    ): Observable<any> => {
        if (!options) {
            options = {};
        }
        if (isFormData) {
            options.headers = new HttpHeaders({});
        } else if (!options.headers && !isFormData) {
            options.headers = new HttpHeaders({
                "Content-Type": "application/json",
            });
        }
        if (authenticated) {
            let userDetails = this.helperService.getStorage("users");
            userDetails = userDetails ? userDetails : {};
            options.headers = options.headers.set(
                "authToken",
                `${userDetails["authToken"]}`
            );
        }
        let url = `${this.portalGateway}${this.apiVersion}${service}${endpoint}`;
        // html query for "GET", json body for others.
        if (queryOptions && typeof queryOptions === "object") {
            const queryParams = [] as string[];
            Object.keys(queryOptions).map((key) => {
                queryParams.push(`${key}=${(queryOptions as any)[key]}`);
                return key;
            });
            url += `?${queryParams.join("&")}`;
        }

        switch (method) {
            case "GET":
                return this.http.get(url, options);
            case "POST":
                return this.http[method.toLowerCase()](url, body, options);
            case "PUT":
                return this.http[method.toLowerCase()](
                    url,
                    JSON.stringify(body),
                    options
                );
            case "DELETE":
                return this.http[method.toLowerCase()](url, options);
        }
    };

   
}

I am expecting modification in flow so that every response will get decrypted before reaching it to subscription


Solution

  • It is exactly like you mentioned - interceptor.

    This is a typical use case for using a response interceptor so that you can modify the response before it reaches the functions, if this is a general approach that is followed throughout your application.

    A general approach will be:

    @Injectable()
    export class ResponseTransformerInterceptor implements HttpInterceptor {
      constructor(private decryptionService: DecryptionService) {}
    
      intercept(request: HttpRequest, next: HttpHandler) {
        return next.handle(request).pipe(
          filter((event): event is HttpResponse => event instanceof HttpResponse),
          map((event) => {
            const eventBody = event.body;
            return event.clone({ body: this.decryptionService.decryptData(eventBody) });
          }),      
        );
      }
    }
    

    This code, however, assumes that you want to decrypt the responses for all API calls in your app.