Introduction:
Angular is a popular Typescript framework by Google used for building web applications. One of the key features of Angular is its ability to make HTTP calls to retrieve data from a server or send data to a server. In this article, we will explore how to perform HTTP calls using an Angular service in a generic way. We will cover the basic concepts, demonstrate the implementation steps, and provide some practical examples.
Table of Contents:
1. What is Angular Service?
2. Setting up a Angular Http Client
3. Making basic Http request
4. Typed Http request
5. Creating an Angular Service
6. Why service and why to make generic?
7. Http call using service
8. Making it Generic
1. What is Angular Service?
Before we dive into HTTP calls, it's important to understand the role of an Angular service. Services in Angular are used to encapsulate and share functionality across components. They provide a way to centralize common logic, such as making HTTP requests, so that it can be easily reused throughout an application.
2. Setting Up the Angular HTTP Client
To begin making HTTP calls, we need to set up the Angular HTTP client. We'll discuss how to import and configure the HTTP client module in your Angular project.
In app.module.ts
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';// <-- import HttpClientModule
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
FormsModule,
ReactiveFormsModule,
HttpClientModule, // <-- add it in import section
],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
3. Making basic Http request
After the setup done in app.module.ts file we can jump onto the component
// app.component.ts
import { HttpClient } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-http',
templateUrl: './http.component.html',
styleUrls: ['./http.component.scss'],
})
export class HttpComponent implements OnInit {
constructor(private http: HttpClient) {
// Basic http GET request
this.http.get('url to get data').subscribe(
(res) => {
console.log(res);
},
(err) => {
console.log(err);
}
);
}
ngOnInit(): void {}
}
Inside the constructor HttpClient has to be injected to make http calls using http
variable that we used to inject.
http
provide basic http calls like GET, POST, PUT, DELETE, PATCH. These are the basic http calls we need.
// GET request
this.http.get('url').subscribe();
// POST request
this.http.post('url', { dataToSend }).subscribe();
// PUT request
this.http.put('url', { dataToSend }).subscribe();
// DELETE request
this.http.delete('url', { dataToSend }).subscribe();
// PATCH request
this.http.delete('url').subscribe();
subscribe()
you can see that there is a subscribe function is called on every http calls. subscribe function tells browser to make request to the url
with data that you've given. To make it simple creating connection using http
and opening/ calling the request using subscribe()
.
It is required to use subscribe function so that we can get response and error back from the server.
this.http.get('url').subscribe(
(res) => {
console.log(res);
},
(err) => {
console.log(err);
}
);
Inside this subscribe it gives two outputs either resultObject when the request is success or errorObject when request is failure. Using those object we can handle things further.
4. Typed Http request
Now comes the magic 🪄 of the typescript. We can even get the result as we expected by annotate the request with type.
// creating type for Post
interface Post {
userId: number;
title: string;
body: string;
}
this.http.get<Post[]>('url').subscribe(
(res) => {
console.log(res); // <-- this res will be in the type of Post array
},
(err) => {
console.log(err);
}
);
Since the above get request in annotated with <Post[]>
() the subscribe function result in the type of Post[]
so the result will in the type of array of Post object and every object will contain userId, title and body.
This goes to all http request.
5. Creating an Angular Service
We can generate a service in angular by,
ng generate service {your-service-name}
6. Why service and why to make generic?
Service is basically used to reduce the repetition of the code in angular. It can easily injected in any component and can directly access the functions variables inside the service.
Now coming to the main point Why http calls have to be in service and why it has to be a generic service.
-
http
calls are repeated in many places. - Since every object commonly have all GET, PUT, POST, DELETE requests, making it a generic service and inheriting it make our work easier, simpler and cleaner.
7. Http call using service
Using generate command create a service.
// post.service.ts
import { Injectable, EventEmitter } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class PostService {
getPost(id: number): Observable<Post> {
return this.client.get<Post>(`{apiUrl}/posts/${id}`);
}
getPosts(): Observable<Post[]> {
return this.client.get<Post[]>(`{apiUrl}/posts`);
}
putPost(id: number, data: Post): Observable<Post> {
return this.client.put<Post>(`{apiUrl}/posts/${id}`, data);
}
postPost(data: Post): Observable<Post> {
return this.client.post<Doctor>(`{apiUrl}/posts`, data);
}
deletePost(id: number): Observable<Post> {
return this.client.delete(`{apiUrl}/posts/${id}`);
}
}
@Injectable
tells angular that it is injectable class (i.e) to create a new object of the class.
The class PostService
can be injected in any component and the functions like getPost, getPosts, putPost, postPost, deletePost can be used.
// app.component.ts
import { HttpClient } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
import { PostService } from './service/post.service.ts'; // <-- import service
@Component({
selector: 'app-http',
templateUrl: './http.component.html',
styleUrls: ['./http.component.scss'],
})
export class HttpComponent implements OnInit {
constructor(private post: PostService) { // <-- inject the service
// Basic http GET request
this.post.getPosts().subscribe( // <-- use it where ever needed
(res) => {
console.log(res);
},
(err) => {
console.log(err);
}
);
}
ngOnInit(): void {}
}
By injecting the service in the component we can access its methods. Since it returns an Observable
we need to subscribe it for further computations.
But, the problem is we need to create the same thing for the other objects like Post.
Let's say we need to create a service for Comment. What we need to do is create another service and define everything that we did in post.service.ts. So what we can do is making an abstract Generic service and by inheritance we can easily achieve what we need in easiest way
8. Making it Generic
// generic.service.ts
function createOptions(options: any) {
if (!options) return '';
let string = '?';
Object.keys(options).forEach((key, id, s) => {
if (s.length == id) string += `${key}=${options[key]}`;
else string += `${key}=${options[key]}&`;
});
return string;
}
export abstract class GenericService<T> {
private baseUrl = environment.apiUrl;
constructor(private httpClient: HttpClient, protected apiUrl: string) {}
public create(resource: Partial<T>): Observable<T> {
return this.httpClient.post<T>(`${this.baseUrl}${this.apiUrl}`, resource);
}
public get(options?: any): Observable<T[]> {
return this.httpClient.get<T[]>(
`${this.baseUrl}${this.apiUrl}${createOptions(options)}`
);
}
public getAll(options?: any): Observable<T> {
return this.httpClient.get<T>(
`${this.baseUrl}${this.apiUrl}${createOptions(options)}`
);
}
public getById(id: number): Observable<T> {
return this.httpClient.get<T>(`${this.baseUrl}${this.apiUrl}/${id}`);
}
public update(resource: Partial<T>, id: number): Observable<T> {
return this.httpClient.put<T>(
`${this.baseUrl}${this.apiUrl}/${id}`,
resource
);
}
public delete(id: number): Observable<void> {
return this.httpClient.delete<void>(`${this.baseUrl}${this.apiUrl}/${id}`);
}
}
By extending this Generic service in every other class we can achieve all the api calls.
// api.service.ts
@Injectable({ providedIn: 'root' })
export class PostService extends GenericService<Post> {
constructor(private http: HttpClient) {
super(http, `/posts`);
}
}
@Injectable({ providedIn: 'root' })
export class CommentService extends GenericService<Comment> {
constructor(private http: HttpClient) {
super(http, `/comments`);
}
}
This PostService and CommentService will provide same functions provided by old service.
By extending this Generic Service we can create as many service we need.
Happy Coding 💻 :).
Top comments (3)
Excellent explanation with easily understandable examples..! helped alot..!
Clearly explained on the topic. Nice post
Clear cut explanation. Excellent work.
Expecting more blogs like this.