Search code examples
angulartypescriptrxjsangular-httpangular2-observables

Angular4 - Central error Logging and handling for all http requests


I have developed the following wrapper class for all my http calls. I have just included the get function in the example

import { Injectable } from '@angular/core';
import { HttpClient, HttpParams, HttpResponse, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';

/**
 * Wrapper around the Http provider to allow customizing HTTP requests
 */
@Injectable()
export class HttpClientService {
    private httpParams: HttpParams;
    private httpHeaders: HttpHeaders;

    constructor(
        private httpClient: HttpClient,
        private sharedService: SharedService) {
        this.httpParams = new HttpParams();
        this.httpHeaders = new HttpHeaders({ 'Access-Control-Allow-Origin': '*' });

    }

    public get<T>(url: string, httpParams: Array<Map<string, string>>) {
        return this.httpClient
            .get<T>(url, { params: this.appendHttpParams(httpParams), headers: this.httpHeaders })
            .subscribe(data => {
                console.log(data);
            },
            err => {
                console.log(err);
            });

    }

    private appendHttpParams(paramArray: Array<Map<string, string>>): HttpParams {
        paramArray.forEach((value: Map<string, string>, index: number, array: Array<Map<string, string>>) => {
            this.httpParams.append(value.keys[index], value.values[index]);
        });
        return this.httpParams;

    }
}

This works fine. But when I try to call the get from a custom service as follows

this.httpClientService.get<StoredAppData[]>(this.configService.urls.fetchSettings, params)
    .map((response) => {
        this.storedAppData = response.json();
        console.log(this.storedAppData);
        return this.storedAppData;
    });

It throws a TS2339: Property 'map' does not exist on type 'Subscription'error. I understand that I have already subscribed to the Observable and would work well if I get rid of the .subscribe() and just return the function. However, then I cannot implement central error handling on a single layer. What could be a good way to do it?


Solution

  • Type error addresses actual problem in code. A subscription shouldn't be returned from a method that is supposed to return an observable:

    const response$ = this.httpClient.get<T>(...)
    response$.subscribe(data => ..., err => ...);
    return response$;
    

    Unless returning a hot observable is a desirable effect, subscribe shouldn't be performed in service itself at all. Instead, do operator should be used for side effects:

    This operator is useful for debugging your Observables for the correct values or performing other side effects.

    Note: this is different to a subscribe on the Observable. If the Observable returned by do is not subscribed, the side effects specified by the Observer will never happen. do therefore simply spies on existing execution, it does not trigger an execution to happen like subscribe does.

    return this.httpClient.get<T>(...)
    .do(data => ..., err => ...);