HttpClient_ Angular

 

Angualar HttpClient - Complate interview Guide

What is HttpClient?

HttpClient: is a built-in Angualr service for making HTTP requests to backend APIs. It's part of @angular/commom/http module and provides a simplified API for HTTP functinality.

Package Location

import { HttpClient } form '@angular/common/http';

Key Features of HttpClient

  1. ✅ Typed Response: - Supports TypeScript generics for type safe
  2. ✅ Observable: - Return RxJS Observable (cancellable, composable)
  3. ✅ Request/Response Intereception: - Via HTTP Interceptors
  4. ✅ Testability: - Easy to mock and test with HttpClientTestingModule
  5. ✅ Progress Events: - Track Upload/download progress
  6. ✅ Error Handeling: - Structured error handling with HttpErrorResponse
  7. ✅ Request Cancellation: - Using unsubscribe
  8. ✅ Automatic JSON Parsing: - Parses JSON response automatically

HttpClient vs Http (Deprecated)

FeatureHttpClient (current)Http (old)
Package@angular/common/http@angular/http
Response TypeObservableObservable
JSON Parsing✅ Automatic❌ Manual (.json())
Interceptors✅ Yes❌ No
Type Safety✅ Active❌ Deprecated
Request/ResponseImmutableMutable
Status✅ Active❌ Deprecated
Tree-Shakable✅ Yes❌ No

Setup HttpClient in Angular Application

Method 1: Using provideHttpClient (Standalone - Modern)

// app.config.ts
import { ApplicationConfig } from '@angular/core';
import {ProvideHttpClient, withInterceptors} from '@angular/common/http';
import { authInterceptor } from './interceptors/auth.interceptor';
import { loggingInterceptor } from './interceptors/logging.Interceptor';

export const appConfig: ApplicationConfig = {
    providers: [
        provideHttpClient(
            withInterceptors([
                loggingInterceptor,
                authInterceptor
            ])
        )
    ]
};

Method 2: Using HttpClientModule (NgMoudule - lagacy)

// app.module.ts
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';

@NgModule({
    imports: [
        HttpClientModule
    ],
    providers: []
})
export class AppModule { }

HTTP Methods Available

| Method | Purpose | Idempotent | Safe | Example Use case | | GET | Retrieve data | ✅ Yes | ✅ yes | Fetch user list | | POST | Create resource | ❌ No | ❌ No | Create user | | PUT | Replace resource | ✅ Yes | ❌ No | Update user completely | | PATCH | Partica Update | ❌ No | ❌ No | Update some field only | | DELETE | Delete resource | ✅ Yes | ❌ No | Delete user | | HEAD | Get headers only | ✅ Yes | ✅ Yes | check if resource exists | | OPTIONS | Get all methods | ✅ Yes | ✅ Yes | CORS preflight |

Note:

  • Idempotent: Multiple identical requests have same effect as single request
  • Safe: Request doesn't modify server state

Complate Implementation Examples

1. Create User Service with All HTTP Methods

// ../services/user.service.ts
import { Injectable } from '@angualar/core';
import { HttpClient, HttpHeaders, HttpParams, HttpErrorResponse } from '@angular/commom/http';
import { Observable, throwError } from 'RxJs';
import { catchError, retry, map, tap } from 'rxjs/operators';

// Define Interfaces for type safe
export interface User {
    id?: number;
    name: string;
    email: string;
    phone?: string;
    website?: string;
    address?: {
        street: string;
        city: string;
        ZipCode: string;
    };
}

export interface ApiResponse<T> {
    data: T;
    message: string;
    status: number;
}

export interface PaginatedResponse<T> {
    items: T[];
    total: number;
    page: number;
    PageSize: number;
}

@Injectable({
    providedIn: 'root'
})
export class UserService {
    private apiUrl = 'https://jsonplaceholder.typicode.com/users';

    constructor (private http: HttpClient) { }

    //======Get Request ======

    // Basic GET - Fetch all users
    getUsers(): Observable<User[]> {
        return this.http.get<User[]>(this.apiUrl).pipe(
            retry(2); //Retry failed request 2 times
            tap(users => console.log('Fetched User: ', users.length)),
            catchError(this.handleError)
        );
    }
    // GET with Query Parameters
    getUserWithId(id: number): Observable<User>{
        return this.http.get<User>(`${this.apiUrl}/${id}`).pipe(
            tap(user => console.log('Fetched User: ', user.name)),
            catchError(this.handleError)
        );
    }

    // Get with Query Paramaters
    getUsersWithParams(page: number = 1, limit: number= 5): Observable<user[]>{
        const params = new HttpParams()
            .set('_page', page.toString())
            .set('_limit', limit.toString());

        return this.http.get<User[]>(this.apiUrl, { params }).pipe(
            catchError(this.toString());
        );
    }

    // Get With Multiple Query Parameters
    searchUsers(filters: {name?:string; email?: string}): Observable<User[]>{
        let params = new HttpParams();
        if(filters.name){
            params = params.set('Name', filters.name);
        }
        if(filters.Eamil){
            params = params.set('email', filters.email);
        }

        return this.http.get<User[]>(this.apiUrl, { params }).pipe(
            catchError(this.handleError)
        );
    }

    //Get With Custom Headers
    getUserswithHeaders(): Observable<User[]> {
        const headers = new HttpHeaders({
            'Content-Type': 'application/json',
            'x-Custom-Header': 'CustomValue',
            'Autherization': 'Bearer token123'
        });

        return this.http.get<User[]>(this.apiUrl, {headers}).pipe(
            catchError(this.handleError);
        );
    }

    // GET with full Response (including headers and status)
    getUsersFullResponse(): Observable<any> {
        return this.http.get(this.apiUrl, { observe: 'response' }).pipe(
            map(response => {
                console.log('Status Code:', response.status);
                console.log('Status Text:', response.statusText);
                console.log('Headers', response.headers.keys());
                console.log('Body:', response.body);
            }),
            catchError(this.handleError)
        );
    }
    // GET as Text (not JSON)
    getUsersAsText(): Observable<string> {
        return this.http.get(this.apiUrl, { responseType: 'text' }).pipe(
            catchError(this.handleError)
        );
    }

    // GET as Blob (for file downloads)
    downloadUserReport(): Observable<Blob> {
        return this.http.get(`${this.apiUrl}/report`, { responseType: 'blob'}).pipe(
            catchError(this.handleError)
        );
    }

    //========== POST REQUEST ==============

    // Basic POST - Create new User
    createUser(user: User): Observable<User> {
        return this.http.post<User>(this.apiUrl, user).pipe(
            tap(newUser => console.log('Created user with ID', newUser.id)),
            catchError(this.handleError)
        );
    }

    // POST with Headers
    createUserWithHeaders(user: User): Observable<User> {
        const headers = new HttpHeaders({
            'Content-Type':'application/json',
            'Autherization': 'Bearer token123'
        });

        return this.http.post<USer>(this.apiUrl, user, { headers }).pipe(
            catchError(this.handleError)
        );
    }

    // POST with full Response
    cerateUserFullResponse(user: User): Observable<any> {
        return this.http.post(this.apiUrl, user, {observe: 'response'}).pipe(
            map(response => {
                map(response => {
                console.log('Status Code:', response.status);
                console.log('Status Text:', response.statusText);
                console.log('Headers', response.headers.keys());
                console.log('Body:', response.body);
            }),
            catchError(this.handleError)
            })
        );
    }
    // POST FormData (for file upload)
    uploadUserwithFile(user: User, file: File): Observable<any>{
        const formData = new FormData();
        formdata.append('user', JSON.stringify(user));
        formdata.append('file', file, file.name);

        return this.http.post(`${this.apiUrl}/upload`, formdata).pipe(
            catchError(this.handleError)
        );
    }

    //===========PUT REQUEST=============

    // PUT - Complete replecment of resource 
    updateUser(id: number, user: User): Observable<User>{
        return this.http.put<User>(`${this.apiUrl}/${id}`, user).pipe(
            tap(updatedUser => console.log('Update user', updatedUser)),
            catchError(this.handleError)
        );
    }

    // PUT with headers
    updateUserWithHeradrs(id: number, user: User): Observable<User> {
        const headers = new HttpHeaders({
            'Content-Type': 'application/json',
            'If-Match': 'etag-value' // For optimistic locking
        });

        return this.http.put<User>(`${this.apiUrl}/${id}`, user, { headers }).pipe(
            catchError(this.handleError)
        );
    }

    //===============Patch REQUESTS===============

    //PATCH - Partical update 
    patchUser(id: number, updates: Partial<User>): Observable<User> { 
        tap( patchedUser => console.log ('Patched user:', patchedUser)).pipe(
        catchError(this.handleError)
        );
    }
    // PATCH specific field
    updateUserEmail(id: number, email: string): Observable<User> {
        return this.http.patch<User>(`${this.apiUrl}/${id}`, {email}).pipe(
            catchError(this.handleError)
        );
    }

    //================Delete REQUEST=================
    // Basic Delete
    deleteUser(id: number): Observable<void> {
        return this.http.delete<void>(`${this/apiUrl}/${id}`).pipe(
            tap(()=> consloe.log(`Deleted user with ID:`)),
            catchError(this.handleError)
        );
    }
    //Delete with Response
    deleteUserWithResponse(id: number): Observable<any> {
        return this.http.delete(`${this.apiUrl}/${id}`, {observe: 'response'}).pipe(
            map( response => {
                console.log('Deleted Status:', response.status);
                return response;
            }),
            catchError(this.handleError)
        );
    }

}

Comments

Popular posts from this blog