Creating Versatile Functions for HttpClient Wrapping

Scenario:

In my set of services, I find myself repeatedly writing code for data calls which results in a lot of duplicated code. To streamline the process and reduce redundancy, I am looking to implement a wrapper function:

All these functions essentially perform the same task:

  1. They require a return parameter.
  2. They utilize either a post/get/delete method.
  3. They return a promise.

Here is an example of how I tried to create a generalized wrapper function specifically for the get method:

public httpGetPromise<T extends any>(endpoint: string, returnType: T): Promise<T> {
    const promise: Promise<returnType> = new Promise<returnType>((resolve,reject) => {
        this.http.get<returnType>(`${this.endpointBaseUri}+${endpoint})
        .toPromise().then((response) => {
             resolve(response);
        }, (err) => {
             reject(response);
        });
    });
    return promise;
}

While this does simplify the process somewhat, I am convinced that there are better approaches available.

Is there a more efficient way to write this wrapper function to make it more versatile and adaptable to different input types?

Below are snippets demonstrating typical Get / Post / Delete functions without the use of a wrapper:

public saveMachine(newMachine: Machine): Promise<Machine> {
    // Code for saving a machine
}

public deleteMachine(machine: Machine): Promise<Machine> {
    // Code for deleting a machine
}

public getMachinesConfigs(machineId: string): Promise<MachineConfig[]> {
   // Code for retrieving machine configurations
}

As evident from the provided examples, there is ample room for generalizing these functions using wrappers.

With the implementation of my suggested wrapper function for 'get', the calls would look something like this:

public getMachinesConfig(machineId:string, MachineConfig[]): MachineConfig[] {
     // Implementation using the wrapper function
}

I am using TypeScript version 3.2.4.

Sidenote: Is it feasible to pass along the HTTP method type within the wrapper parameters? For instance:

public promiseFunc(httpMethod:HttpClient,..., data?:any, etc...)

This way, a single function could cater to all post, get, and delete promises efficiently.

Answer №1

Definitely! You have the option to utilize a generic type parameter in your function:

public fetchData<T>(endpoint: string){
    const promise: Promise<T> = new Promise<T>((resolve,reject) => {
        this.http.get<T>(`${this.endpointBaseUri}${endpoint}`)
        .toPromise().then((response) => {
             resolve(response);
        }, (err) => {
             reject(reject);
        });
    });
    return promise;
}

this.fetchData<MachineConfig[]>(...)

Additionally, you can simplify by eliminating the promise constructor and directly returning the Promise from toPromise:

public fetchData<T>(endpoint: string){
    return this.http.get<T>(`${this.endpointBaseUri}${endpoint}`)
        .toPromise();
}

Your usage example would be like:

public getMachineConfigs(machineId:string): MachineConfig[] {
     const endpoint: string = `/machines/${machineId}/machineconfigs`;
     return this.wrapperService.fetchData<MachineConfig[]>(endpoint);
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Dynamically attach an Angular 2+ directive from a component

Looking to dynamically attach a directive from a component based on a condition. My attempt using @HostBinding doesn't seem to be working. import { Component, Directive, HostBinding } from '@angular/core'; @Component({ selector: &apos ...

A guide to submitting comments with Angular 2 using a GitHub OAuth token

I'm attempting to utilize a GitHub OAuth Token for posting comments in Angular 2. Below is the code I am using: postComment(token: string, number: Number, body: string): Promise<Comment> { let headers = new Headers() headers.append('Auth ...

What is the reason for observables not being subscribed to in NgRx effects?

My understanding was that in Angular, observables will not run unless they are subscribed to. However, when examining rxjs effects, the coding often appears like this: getProcess$ = createEffect(() => this.actions$.pipe( ofType(ProcessActions. ...

Displaying maximum number of items in each row using ngFor

Is there a way to automatically create a new row within the 'td' element after the ngFor directive has generated 10 image repeats? Currently, all the images are displayed in a single td and as more images are added, they start to shrink. <td ...

What is the best way to use two distinct CSS styles for mat-form-field across multiple pages?

On one of my pages, I have a mat-form-field: <mat-form-field class="form-control-full-width"> <input type="text" matInput placeholder="First Name" formControlName="firstNameFC" required> <mat-error *ngIf="hasNewUserErro ...

Every field displaying a description above the input

Is there a way to customize the default wrapper for all fields? I am looking to have the description displayed above the fields instead of below them, which is the default behavior for Bootstrap. Check out this StackBlitz example: https://stackblitz.com/ ...

Is it possible to have a button within a table that, when clicked, opens a card overlaying the entire table?

I'm having an issue with a table and card setup. When I click the button in the table, the card that appears only covers part of the table. I want it to cover the entire table area based on the content inside the card. How can I make this happen? I&a ...

Angular Material's *matNoDataRow directive is malfunctioning

I am having an issue with using the *matNoDataRow directive in Angular Material. I have created a MatTable with filtering functionality, and when no data matches the filter, I want to display a specific text. However, the directive does not seem to be work ...

A Guide to Launching the Angular 2 Quick Start Project on a Linux (CentOs) System

I'm struggling with deploying this on CentOS using a daemon thread. Currently, I can only initiate it with: npm start. However, I want it to automatically start without needing my manual intervention! Thank you for any assistance. I attempted to foll ...

Decipher the splitButton tag from PrimeNG

I am currently attempting to translate items from "p-splitButton", but I am facing a challenge because the "items" is an object. How can I accomplish this task? [model]="items | translate" app.component.html <p-splitButton label="Save" icon="pi pi- ...

Encountering an issue when attempting to save an excel file in Angular 8, receiving an error message that states "

When working with angular 8, I encountered an issue while trying to save an excel file. The error message displayed was as follows: ERROR TypeError: Failed to execute 'createObjectURL' on 'URL': Overload resolution failed. at Functi ...

Guide to generating TypeScript output files within a non-hierarchical directory layout

In my project, I have a directory structure with multiple typescript files organized as follows: | src | app-1 | tsconfig.json | app-2 | tsconfig.json | common | standalone | tsconfig.json For each of the ...

Combining two objects/interfaces in a deep merging process, where they do not intersect, can result in a final output that does not

When attempting to merge two objects/interfaces that inherit from the same Base interface, and then use the result in a generic parameter constrained by Base, I encounter some challenges. // please be patient type ComplexDeepMerge<T, U> = { [K in ( ...

Is OnPush Change Detection failing to detect state changes?

Curious about the issue with the OnPush change detection strategy not functioning properly in this demonstration. My understanding is that OnPush change detection should activate when a property reference changes. To ensure this, a new array must be set e ...

Creating a unique type with a suffix of `px`

Understanding how to create a Position type class is clear: class Position { x: number = 0; y: number = 0; } However, I now require the x and y values to be integers with the suffix of px, like this: const position = { x: '1px', y: &ap ...

Encountering an ERROR of TypeError when attempting to access the property 'length'

I encountered the following error message: ERROR TypeError: Cannot read property 'length' of undefined at eval (webpack-internal:///./node_modules/@angular/common/esm5/http.js:163) at Array.forEach () at HttpHeaders.lazyInit ...

How can I access members outside of a class without a name in Typescript?

I recently developed an "anonymous" class inspired by this insightful discussion on Typescript anonymous classes. However, I'm facing a challenge in accessing the outer scope members. Here's a snippet of my code for context: class BaseCounter { ...

The current version of npm for @angular 2 has not been released yet

Looking to transition from Angular 2 Beta 15 to Angular 2 RC1. Currently utilizing Visual Studio 2015. Within my npm package.json in Visual Studio, I've inputted: "dependencies": { "@angular/core": "Unavailable", } However, it displays as unav ...

Javascript Library Issue: "Implicitly Declared Type 'Any' Error"

I am currently in the process of developing a JavaScript library that will interact with an API. My goal is to create a module that can be easily published on npm and utilized across various frameworks such as Angular or React. Below is the code snippet fo ...

Searching for and removing array elements in Angular 2 using the IndexOf method

Hey there! I'm currently in the process of trying to remove a specific item from my array while working with Angular2 and Typescript. My goal is to identify the index based on the value provided. The array I am working with is initialized as follows. ...