HTTP Client Features
Angular’s HTTP client is a core tool for communicating with backend services. It provides methods to send HTTP requests and receive responses, including GET, POST, PUT, and DELETE. Built on RxJS, Angular’s HTTP client simplifies handling asynchronous data streams.
Importing HttpClientModule
To use the HTTP client, import HttpClientModule in AppModule by adding it to the @NgModule decorator’s imports array.
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
HttpClientModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }Using HttpClient
The HttpClient class provides methods for sending HTTP requests:
get(): Sends a GET request.post(): Sends a POST request.put(): Sends a PUT request.delete(): Sends a DELETE request.
These methods return an Observable, which you can subscribe to for handling response data.
Creating an HTTP Service
Encapsulate backend communication in a dedicated HTTP service. Here’s an example:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class HttpService {
private apiUrl = 'https://api.example.com';
constructor(private http: HttpClient) { }
getData(): Observable<any> {
return this.http.get(`${this.apiUrl}/data`);
}
postData(data: any): Observable<any> {
return this.http.post(`${this.apiUrl}/data`, data);
}
}Handling Responses
Inject the HTTP service into a component and subscribe to its methods to process response data:
import { Component } from '@angular/core';
import { HttpService } from './http.service';
@Component({
selector: 'app-root',
template: `
<h1>Data</h1>
<ul>
<li *ngFor="let item of items">{{ item.name }}</li>
</ul>
`,
styleUrls: ['./app.component.css']
})
export class AppComponent {
items: any[];
constructor(private httpService: HttpService) {
this.httpService.getData().subscribe(
response => {
this.items = response;
},
error => {
console.error('Error fetching data:', error);
}
);
}
}Error Handling
Errors are typically handled in the error callback of a subscription. Use try/catch or RxJS’s catchError operator:
import { catchError } from 'rxjs/operators';
import { throwError } from 'rxjs';
getData(): Observable<any> {
return this.http.get(`${this.apiUrl}/data`).pipe(
catchError(error => {
console.error('Error fetching data:', error);
return throwError('Something went wrong; please try again later.');
})
);
}Custom HTTP Headers
Customize HTTP request headers, such as adding authentication tokens:
postData(data: any): Observable<any> {
const headers = { 'Content-Type': 'application/json', 'Authorization': 'Bearer token' };
return this.http.post(`${this.apiUrl}/data`, data, { headers });
}Using Interceptors
Interceptors allow intercepting and modifying HTTP requests and responses, useful for global error handling, logging, or header modifications:
import { Injectable } from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const authReq = req.clone({ headers: req.headers.set('Authorization', 'Bearer token') });
return next.handle(authReq);
}
}Register the interceptor in AppModule:
@NgModule({
// ...
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }
]
})
export class AppModule { }Summary
Angular’s HTTP client offers a robust and flexible API for backend communication. Using HttpClient, you can easily send various HTTP requests and handle responses via Observables. Creating dedicated HTTP services, handling errors, customizing headers, and using interceptors help build robust and maintainable Angular applications.
Setting Up HttpClient
Angular’s HttpClient is a powerful tool for handling HTTP requests and responses with backend servers. This section guides you through setting up and using HttpClient, covering basic configuration, request types, error handling, custom headers, interceptors, and advanced topics.
Importing HttpClientModule
Import HttpClientModule in AppModule’s imports array:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
HttpClientModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }Creating an HTTP Service
Create a service to encapsulate HTTP requests, keeping components clean and enabling reuse:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class ApiService {
private baseUrl = 'https://api.example.com';
constructor(private http: HttpClient) { }
getItems(): Observable<any[]> {
return this.http.get<any[]>(`${this.baseUrl}/items`);
}
createItem(item: any): Observable<any> {
return this.http.post(`${this.baseUrl}/items`, item);
}
updateItem(id: number, item: any): Observable<any> {
return this.http.put(`${this.baseUrl}/items/${id}`, item);
}
deleteItem(id: number): Observable<any> {
return this.http.delete(`${this.baseUrl}/items/${id}`);
}
}Using the HTTP Service in Components
Inject the service into a component to send HTTP requests:
import { Component, OnInit } from '@angular/core';
import { ApiService } from './api.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
items: any[] = [];
constructor(private apiService: ApiService) { }
ngOnInit() {
this.getItems();
}
getItems() {
this.apiService.getItems().subscribe(
items => this.items = items,
error => console.error('Error fetching items:', error)
);
}
createItem() {
const newItem = { /* item data */ };
this.apiService.createItem(newItem).subscribe(
() => this.getItems(),
error => console.error('Error creating item:', error)
);
}
}Error Handling
Handle errors in the subscribe method’s error callback:
apiService.getItems().subscribe(
items => this.items = items,
error => {
console.error('Error fetching items:', error);
alert('Failed to fetch items.');
}
);Custom Request Headers
Pass an options object to customize headers:
updateItem(id: number, item: any): Observable<any> {
const headers = { 'Content-Type': 'application/json', 'Authorization': 'Bearer your-token' };
return this.http.put(`${this.baseUrl}/items/${id}`, item, { headers });
}Using Interceptors
Interceptors modify HTTP requests and responses, useful for global error handling or header modifications:
import { Injectable } from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable()
export class TokenInterceptor implements HttpInterceptor {
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const authRequest = request.clone({ headers: request.headers.set('Authorization', 'Bearer your-token') });
return next.handle(authRequest);
}
}Register in AppModule:
@NgModule({
// ...
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: TokenInterceptor, multi: true }
]
})
export class AppModule { }Using Forms and FormData
Use FormData for file uploads or form data:
uploadFile(file: File): Observable<any> {
const formData = new FormData();
formData.append('file', file);
return this.http.post(`${this.baseUrl}/upload`, formData);
}Using JSONP
For APIs not supporting CORS, use JSONP:
fetchJsonp(url: string): Observable<any> {
return this.http.jsonp(url, 'callback');
}Performance Optimization
- HTTP Caching: Cache frequently accessed data to reduce network requests.
- Lazy Loading: Use lazy loading and dynamic imports to reduce initial load time.
Testing
Write unit tests to ensure HTTP services work as expected:
it('should fetch items', () => {
const items = [{ id: 1, name: 'Item 1' }];
service.getItems().subscribe(data => expect(data).toEqual(items));
expect(httpSpy.get).toHaveBeenCalledWith('https://api.example.com/items');
});Making Requests
Angular’s HttpClient module is a robust tool for handling HTTP requests, built on RxJS for reactive server communication.
Importing HttpClientModule
Ensure HttpClientModule is imported in AppModule:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
HttpClientModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }Creating an HTTP Service
Encapsulate HTTP requests in a dedicated service:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class ApiService {
private apiUrl = 'https://api.example.com';
constructor(private http: HttpClient) { }
// GET request
getItems(): Observable<any[]> {
return this.http.get<any[]>(`${this.apiUrl}/items`);
}
// POST request
createItem(item: any): Observable<any> {
return this.http.post(`${this.apiUrl}/items`, item);
}
// PUT request
updateItem(id: number, item: any): Observable<any> {
return this.http.put(`${this.apiUrl}/items/${id}`, item);
}
// DELETE request
deleteItem(id: number): Observable<any> {
return this.http.delete(`${this.apiUrl}/items/${id}`);
}
}Making a GET Request
GET requests retrieve data from the server:
import { Component, OnInit } from '@angular/core';
import { ApiService } from './api.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
items: any[];
constructor(private apiService: ApiService) { }
ngOnInit() {
this.getItems();
}
getItems() {
this.apiService.getItems().subscribe(
items => this.items = items,
error => console.error('Error fetching items:', error)
);
}
}Making a POST Request
POST requests send data to the server:
createItem() {
const newItem = { /* item data */ };
this.apiService.createItem(newItem).subscribe(
() => this.getItems(),
error => console.error('Error creating item:', error)
);
}Making a PUT Request
PUT requests update resources on the server:
updateItem(id: number) {
const updatedItem = { /* updated item data */ };
this.apiService.updateItem(id, updatedItem).subscribe(
() => this.getItems(),
error => console.error('Error updating item:', error)
);
}Making a DELETE Request
DELETE requests remove resources from the server:
deleteItem(id: number) {
this.apiService.deleteItem(id).subscribe(
() => this.getItems(),
error => console.error('Error deleting item:', error)
);
}Error Handling
Prepare for potential errors during requests:
apiService.getItems().subscribe(
items => this.items = items,
error => {
console.error('Error fetching items:', error);
alert('Failed to fetch items.');
}
);Custom Request Headers
Include specific headers, such as authentication tokens:
updateItem(id: number, item: any): Observable<any> {
const headers = { 'Content-Type': 'application/json', 'Authorization': 'Bearer your-token' };
return this.http.put(`${this.apiUrl}/items/${id}`, item, { headers });
}Uploading Files with FormData
Use FormData for file uploads:
uploadFile(file: File): Observable<any> {
const formData = new FormData();
formData.append('file', file);
return this.http.post(`${this.apiUrl}/upload`, formData);
}Using HTTP Interceptors
Interceptors modify requests or responses, such as adding global headers or handling errors:
import { Injectable } from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable()
export class TokenInterceptor implements HttpInterceptor {
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const authRequest = request.clone({ headers: request.headers.set('Authorization', 'Bearer your-token') });
return next.handle(authRequest);
}
}Register in AppModule:
@NgModule({
// ...
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: TokenInterceptor, multi: true }
]
})
export class AppModule { }Using JSONP
For APIs without CORS support, use JSONP:
fetchJsonp(url: string): Observable<any> {
return this.http.jsonp(url, 'callback');
}Performance Optimization
- HTTP Caching: Cache frequently accessed data to reduce network requests.
- Lazy Loading: Use lazy loading and dynamic imports to minimize initial load time.
Testing
Write unit tests to verify HTTP services:
it('should fetch items', () => {
const items = [{ id: 1, name: 'Item 1' }];
service.getItems().subscribe(data => expect(data).toEqual(items));
expect(httpSpy.get).toHaveBeenCalledWith('https://api.example.com/items');
});Testing Response Interception
HTTP interceptors in Angular are powerful tools for executing operations before requests are sent to the server or after responses are received. For example, interceptors can add authentication tokens, modify headers, handle errors, or cancel requests. Properly testing interceptors is crucial for ensuring application stability and security.
Creating a Simple HTTP Interceptor
Create an interceptor that adds a global authentication token to every request:
import { Injectable } from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const authRequest = request.clone({ headers: request.headers.set('Authorization', 'Bearer your-token') });
return next.handle(authRequest);
}
}Registering the Interceptor
Register the interceptor in AppModule or another provider module:
@NgModule({
imports: [
BrowserModule,
HttpClientModule
],
declarations: [AppComponent],
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }
],
bootstrap: [AppComponent]
})
export class AppModule { }Testing the Interceptor
Use Angular’s testing tools, such as TestBed and HttpClientTestingModule, to test the interceptor:
import { TestBed } from '@angular/core/testing';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { HttpClient } from '@angular/common/http';
import { AuthInterceptor } from './auth.interceptor';
describe('AuthInterceptor', () => {
let httpMock: HttpTestingController;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }
]
});
httpMock = TestBed.inject(HttpTestingController);
});
afterEach(() => {
httpMock.verify(); // Ensure no outstanding requests
});
it('should add an Authorization header to requests', () => {
const expectedUrl = 'https://api.example.com/data';
const testData = { message: 'Hello, World!' };
// Send a GET request
const http = TestBed.inject(HttpClient);
http.get(expectedUrl).subscribe(data => {
expect(data).toEqual(testData);
});
// Verify the request includes the correct Authorization header
const req = httpMock.expectOne(expectedUrl);
expect(req.request.headers.get('Authorization')).toEqual('Bearer your-token');
// Simulate server response
req.flush(testData);
});
});Testing Error Handling
If the interceptor handles errors, such as uniform error messaging, test this functionality:
import { throwError } from 'rxjs';
import { HttpErrorResponse } from '@angular/common/http';
import { catchError } from 'rxjs/operators';
// Modified interceptor
export class ErrorHandlingInterceptor implements HttpInterceptor {
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(request).pipe(
catchError((error: HttpErrorResponse) => {
console.error('An error occurred:', error.message);
return throwError(error);
})
);
}
}
// Add to test
it('should handle errors correctly', () => {
const expectedUrl = 'https://api.example.com/error';
const http = TestBed.inject(HttpClient);
// Send a GET request
http.get(expectedUrl).subscribe(
() => fail('Expected an error'),
(error: HttpErrorResponse) => {
expect(error.status).toEqual(404);
expect(error.error).toEqual('Not Found');
}
);
// Verify request
const req = httpMock.expectOne(expectedUrl);
req.flush('Not Found', { status: 404, statusText: 'Not Found' });
});Testing Request Cancellation
If the interceptor cancels requests, test this behavior:
// Modified interceptor
export class CancelInterceptor implements HttpInterceptor {
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (request.url === 'https://api.example.com/cancel') {
return new Observable(subscriber => subscriber.error(new Error('Request was cancelled')));
}
return next.handle(request);
}
}
// Add to test
it('should cancel requests', () => {
const expectedUrl = 'https://api.example.com/cancel';
const http = TestBed.inject(HttpClient);
// Send a GET request
http.get(expectedUrl).subscribe(
() => fail('Expected the request to be cancelled'),
error => {
expect(error.message).toEqual('Request was cancelled');
}
);
// No need to verify request, as it should be cancelled
});



