0
votes

I am designing Api endpoints in Angular using HttpClient. This is a single service class which will handle all the Rest calls, it will be called from other service classes(which are currently making Rest calls).

What is the best practice to make a generic Rest method.

getApi(url: string, options?: object): Observable<any> {
    return this.http.get<any>(url, options).pipe(
      catchError((error: HttpErrorResponse) => {
        return throwError(error);
    })
  )
}

Or

getApi<T>(url: string, options?: object): Observable<T> {
    return this.http.get<T>(url, options).pipe(
      catchError((error: HttpErrorResponse) => {
        return throwError(error);
    })
  )
}

Will any of the above be able to handle : HttpEvent, HttpResponse or other such response?

Should I make multiple methods to handle?

1
you need to know the difference between any and <T> type. look the answer here stackoverflow.com/questions/44023061/…. Personally, I prefer use <T> and avoid how much I can any. But, in some cases, this is impossible - AlleXyS
Only use any when you cannot use T. - Ben
why you just not use directly HttpClient in your case ? - bubbles
@bubbles right now each service is calling api using HttpClient but i wanted a wrapper on top of it so that all Rest calls can be done from one generic service class. - avi
@Ben we can pass <any> if we are using <T> to define the type right? function identity<T>(arg: T): T { return arg; } const v: any = identity<any>(988); So i think defining with generic <T> will be a better option! please let me know your thoughts I am very new to angular and typescript - avi

1 Answers

2
votes

Please note, the answer is very opinion-based.

Before I will show my solution, I want to share the idea beneath it. So, when I am working with TS I expect to have as strict types as possible. So, using any, object, or something like this is distracting in this context. I also want to avoid any extra typecast because it will lead to an error somewhere.

Luckily, TS is quite smart to help me with this goal, because it can identify types depending on the usage.

Based on this we can build something like this:

A very generic class that can everything we need (it can be extended with passing params to fetch, logging, counter whatever you need)

class HttpService {
    get<TData, TResult>(url: string, data?: TData): Promise<TResult> {
        return fetch(url, data)
            .then(x => x.json())
            .catch((err) => console.log(err));
    }
}

But this class should not be used directly. I would advise creating a more strict and specialized service that will isolated fetch logic and provide strict types instead of generics.

type User = {
    name: string;
}

class MyDataService {

    constructor(private readonly _transportService: HttpService) { }
    fetchUserInfo(userId: number): Promise<User> {
        return this._transportService.get('/some/url', userId);
    }
}

Now, I can use MyDataService without any typecasting and type guessing. One more benefit is that this service is very easily testable because you can substitute the transport module however you want.

Hopefully, my answer helped you.