Thanks to visit codestin.com
Credit goes to github.com

Skip to content

how about some simple helper utlities #63638

@PGTBoos

Description

@PGTBoos

Which @angular/* package(s) are relevant/related to the feature request?

No response

Description

Angular's current HTTP client requires developers to use RxJS Observables for all HTTP operations, even simple request-response patterns. This creates unnecessary complexity for common API calls:
Current approach:
typescript// Requires RxJS knowledge and imports
import { firstValueFrom } from 'rxjs';
const result = await firstValueFrom(this.http.get<User[]>('/api/users'));
Impact on developer experience:

Steep learning curve for developers from other frameworks
Verbose code for simple HTTP operations
Cognitive overhead of understanding Observables for basic API calls
Inconsistent patterns across teams (some use .subscribe(), others firstValueFrom)

Proposed Solution
Add simple Promise-based helper functions that wrap the existing HttpClient:
typescript// Proposed simple API
const users = await callApiGet<User[]>('/api/users');
const newUser = await callApiPost('/api/users', { name: 'John' });
Benefits

Lower barrier to entry - Developers familiar with fetch() or other HTTP libraries can be immediately productive
Reduced boilerplate - Eliminates need for RxJS imports and conversions in 80% of HTTP use cases
Consistent patterns - One clear way to make simple API calls
Backward compatibility - Existing HttpClient and Observable patterns remain unchanged
Type safety - Maintains Angular's TypeScript benefits with generic type parameters

Implementation Details

Functions would be thin wrappers around existing HttpClient
No breaking changes to current APIs
Optional adoption - teams can choose when to use them
Maintains all existing HttpClient features (interceptors, error handling, etc.)

Target Audience

New Angular developers coming from other frameworks
Teams building standard CRUD applications
Developers who prefer imperative over reactive programming patterns
Projects where Observable complexity isn't justified

Precedent
Other major frameworks provide simple HTTP APIs by default:

React: fetch() or axios with async/await
Vue: axios or fetch() with Promises
Svelte: fetch() with async/await

This proposal brings Angular's developer experience closer to industry standards while preserving its powerful Observable capabilities for complex scenarios.

Proposed solution

import { inject } from '@angular/core';
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { firstValueFrom } from 'rxjs';

// Simple options interface
export interface SimpleHttpOptions {
headers?: Record<string, string>;
params?: Record<string, string | number | boolean>;
responseType?: 'json' | 'text' | 'blob';
}

// GET helper function
export const callApiGet = <T = any>(url: string, options?: SimpleHttpOptions): Promise => {
const http = inject(HttpClient);

// Build HttpClient options
const httpOptions: any = {};

if (options?.headers) {
httpOptions.headers = new HttpHeaders(options.headers);
}

if (options?.params) {
httpOptions.params = new HttpParams();
Object.keys(options.params).forEach(key => {
httpOptions.params = httpOptions.params.set(key, options.params![key].toString());
});
}

if (options?.responseType) {
httpOptions.responseType = options.responseType;
}

return firstValueFrom(http.get(url, httpOptions));
};

// POST helper function
export const callApiPost = <T = any>(url: string, body: any = null, options?: SimpleHttpOptions): Promise => {
const http = inject(HttpClient);

// Build HttpClient options
const httpOptions: any = {};

if (options?.headers) {
httpOptions.headers = new HttpHeaders(options.headers);
}

if (options?.params) {
httpOptions.params = new HttpParams();
Object.keys(options.params).forEach(key => {
httpOptions.params = httpOptions.params.set(key, options.params![key].toString());
});
}

if (options?.responseType) {
httpOptions.responseType = options.responseType;
}

return firstValueFrom(http.post(url, body, httpOptions));
};

// PUT helper function (bonus)
export const callApiPut = <T = any>(url: string, body: any = null, options?: SimpleHttpOptions): Promise => {
const http = inject(HttpClient);

const httpOptions: any = {};

if (options?.headers) {
httpOptions.headers = new HttpHeaders(options.headers);
}

if (options?.params) {
httpOptions.params = new HttpParams();
Object.keys(options.params).forEach(key => {
httpOptions.params = httpOptions.params.set(key, options.params![key].toString());
});
}

if (options?.responseType) {
httpOptions.responseType = options.responseType;
}

return firstValueFrom(http.put(url, body, httpOptions));
};

// DELETE helper function (bonus)
export const callApiDelete = <T = any>(url: string, options?: SimpleHttpOptions): Promise => {
const http = inject(HttpClient);

const httpOptions: any = {};

if (options?.headers) {
httpOptions.headers = new HttpHeaders(options.headers);
}

if (options?.params) {
httpOptions.params = new HttpParams();
Object.keys(options.params).forEach(key => {
httpOptions.params = httpOptions.params.set(key, options.params![key].toString());
});
}

if (options?.responseType) {
httpOptions.responseType = options.responseType;
}

return firstValueFrom(http.delete(url, httpOptions));
};

Alternatives considered

Alternatives Considered

  1. Do Nothing (Status Quo)
    Description: Keep current Observable-only HTTP approach
    Pros: No additional maintenance burden, maintains consistency
    Cons: Continues developer experience problems, high learning curve for newcomers
    Rejected because: Ignores legitimate usability concerns affecting developer productivity
  2. Replace HttpClient Entirely with Promise-Based API
    Description: Deprecate Observable HttpClient and create new Promise-based client
    Pros: Clean, simple API without Observable complexity
    Cons: Massive breaking change, loses Observable benefits for complex scenarios, huge migration effort
    Rejected because: Would break existing applications and eliminate legitimate use cases for reactive programming
  3. Add Promise Methods Directly to HttpClient
    Description: Extend HttpClient with .getPromise(), .postPromise() methods
    Pros: Integrated into existing service, familiar injection pattern
    Cons: Bloats HttpClient interface, mixing paradigms in same class creates confusion
    Rejected because: Violates single responsibility principle and creates API inconsistency
  4. Third-Party Library Solution
    Description: Let community create separate HTTP libraries (like axios for Angular)
    Pros: No framework maintenance burden, market-driven solutions
    Cons: Fragments ecosystem, bypasses Angular's interceptor system, inconsistent patterns across projects
    Rejected because: Loses integration benefits and creates ecosystem fragmentation
  5. HTTP Utilities Package (Chosen Approach)
    Description: Separate utility functions that wrap existing HttpClient
    Pros:

Optional adoption
No breaking changes
Preserves Observable capabilities
Simple to implement and maintain
Can coexist with existing patterns

Selected because: Provides developer experience improvements without architectural disruption or breaking changes.
This keeps the focus on technical alternatives rather than suggesting the problem is just a matter of education or training.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: common/httpIssues related to HTTP and HTTP Clientarea: testingIssues related to Angular testing features, such as TestBedfeatureIssue that requests a new feature

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions