-1

First of all, my Typescript knowledge is very low, so I assume that this is a somewhat basic question. In the frontend of my web application, there are two parameters which I need to provide in 99% of all http get-requests. To make my life a little bit easier, I decided to write a simple wrapper around HttpClient (from "@angular/common/http") which provides these params for me. All I need to pass to it is an object from which it then reads those two parameters and adds them to the parameters of the get-request. Here is how this code currently looks like:

public get<T>(url: string, additionalObj?: AdditionalObject) {
  if (additionalObj == null) {
    return this.http.get<T>(url, {
      headers: {
        "Content-Type": "application/json",
      },
    });
  } else {
    const params: Record<string, string> = {};
    const param1: string | undefined = additionalObj?.param1;
    const param2: string | undefined = additionalObj?.param2;

    if (param1) {
      params["param1"] = param1;
    }
    if (param2) {
      params["param2"] = param2;
    }

    return this.http.get<T>(url, {
      headers: {
        "Content-Type": "application/json",
      },
      params,
    });
  }
}

While this works just fine in most cases, I also have some functions which need to provide even more params to the get-request, so I need some way to merge the params provided to my function with the two params which I provide inside the function, but I don't know how to do that. I do not want to change anything in the functions which already call my function but I also want to provide a way to specify additional params if necessary. How can I do that?

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
Chris
  • 1,417
  • 4
  • 21
  • 53
  • `params: { ...defaults, ...additionalObj }`? But you don't actually seem to provide any default values in the function, what's this wrapper actually _doing_ for you? – jonrsharpe Jan 17 '22 at 09:49
  • @jonrsharpe What do you mean? It's adding the properties from `AdditionalObject` to the params of the get-request. Right now, there is no way to provide any additional params to this function because it simply wasn't required to do so. However, there is now at least one case where I need to provide params to this function, so I need a way to either append param1 and param2 to the already provided params, or create a new params-object and only add those two params. Does that make it more clear? I think there is some form of missunderstanding, but I can't figure out what exactly it is. – Chris Jan 17 '22 at 09:54
  • If you have more params, why not add them to the `additionalObj`? And why not just use the `additionalObj` _as the `params`_, rather than creating a new object? Are other things defined in `AdditionalObject`? How do you intend to _use_ this in the various cases you want to handle? – jonrsharpe Jan 17 '22 at 10:07
  • @jonrsharpe In this case, the additional params do not belong to `additionalObj`. The `additionalObj` contains some basic data (like tenant information which I use in many different areas of the application), but the params which I need to provide in this case are not related to that. Therefore, I can't use `additionalObj` as the `params`. There are no other things defined in `AdditionalObject`. The most common use case is that I only provide the url to this function. In that case, param1 and param2 are added in the function and the caller just provides `additionalObj`. – Chris Jan 17 '22 at 10:19
  • @jonrsharpe The second (rare) use case is that the caller wants to provide params, too. In that case, The function must receive the params from the caller, add `param1` and `param2` and call the get function from `HttpClient`. Does this make it more clear? – Chris Jan 17 '22 at 10:20
  • It seems like `params: { ...additionalObj, ...maybeMoreParams }` is what you want, your current implementation seems quite overcomplicated. It's not entirely clear your distinction between "I provide" and "the caller provides", either. – jonrsharpe Jan 17 '22 at 10:23
  • @jonrsharpe I see, but I don't know any better due to lack of Typescript knowledge. What would you suggest doing instead? – Chris Jan 17 '22 at 10:24

1 Answers1

0

I think your implementation might be overcomplicated for what you need. I would also look at using Http Interceptors if you're using Angular.

There are a couple of confusing things, you always provide the 2 parameters, but the first branch on your if statement (additionalObj == null) won't use those values? If we make the assumption that you are after technically three branches

  1. No Parameters at all
  2. Two Parameters
  3. Two Parameters + Extra

You can achieve with the following.

Use a helper function to retrieve all keys from a given object and populate a given record. This will save on if (param1) type scenarios.

GetParams(obj: any, params: Record<string, string>): Record<string, string>{
    for (const p of Object.keys(obj)){
      if(p){
        params[p] = obj[p];
      }
    }
    return params;
}

With your wrapper you can alter it as so:

 public get<T>(url: string, additionalObj?: AdditionalObject) {
    if (additionalObj == null) {
      return this.http.get<T>(url, {
        headers: {
          "Content-Type": "application/json",
        },
      });
    } else {
      let params: Record<string, string> = GetParams(additionalObj, {});
    
      return this.http.get<T>(url, {
        headers: {
          "Content-Type": "application/json",
        },
        params,
      });
    }
  }

}

This will get you into the same position as you were, but with slightly cleaner and scalable use

Now to add additional parameters, you have two options,

  1. Modify the existing AdditionalObject properties. If it's too much to change the object, you can slightly change your function's signature for type safety. All you need to do is add the extra parameters to your additionalObj.

public get<T>(url: string, additionalObj?: Partial<AdditionalObject>)

  1. You can add another object to the function call and take those properties.

    public get<T>(url: string, additionalObj?: Partial<AdditionalObject>, evenMoreParams?: any) {
    if (additionalObj == null) {
      return this.http.get<T>(url, {
        headers: {
          "Content-Type": "application/json",
        },
      });
    } else {
      let params: Record<string, string> = GetParams(additionalObj, {});
      params = GetParams(evenMoreParams, params);
    
      return this.http.get<T>(url, {
        headers: {
          "Content-Type": "application/json",
        },
        params,
      });
    }
    

    }

    }

Malcor
  • 2,667
  • 21
  • 29
  • Seems to me like Http Interceptors are actually what I should use in my case. Thanks a lot for making me aware of that! – Chris Jan 17 '22 at 10:54