Hey guys, how are you today?!
It's 2024, let's make this year wonderful and do a lot of some cool things!
Today I'd like to share and talk about a something interesting and it can do your application and development time better!
So let's see and learning something cool!
👾 The problem
When we have an application consuming an API we basically have this structure:
In this example we have a Github News Application consuming an API
import {environment} from "../../environments/environment";
....
export class GithubNewsService {
constructor(
private _httpClient: HttpClient
) { }
getGithubNews(): Observable<GithubNews[]> {
return this._httpClient
.get<GithubNews[]>(`${environment.apiGithubNews}/getNews`);
}
saveNewGithubNews(news: GithubNews): Observable<GithubNews[]> {
return this._httpClient
.post<GithubNews>(`${environment.apiGithubNews}/saveNews`, news);
}
}
If you nice, we have a bit of duplication in our code. In the getGithubNews
and saveNewGithubNews
methods we need to pass the APIU url, which we pass using the environment.apiGithubNews
.
But let's imagine this application becomes increasingly large with countless services consuming the same API url. And this way that we are using it in the future could cause us some problems, because imagine that one day it becomes necessary to change the variable that was previously apiGithubNews
and now has another name, perhaps something like apiGenericNews
we would need changing the name in all files generates work and we could possibly break the application, if we lave somewhere without changing!
And I can say, we can resolve this like a pro and with only a file! Let's see this!
👩🎨 The solution
Angular offer for us a lot of really and powerfull things, and one of these is the HTTPInterceptor
.
🧩 HTTPInterceptor
In a nutshell, with Interceptors
we can transform a flow of events by transforming the request before passing it on, calling next.handle()
and applying the logic we want!
So, thinking about our "problem" we can put the API url of the request in our HTTPInterceptor
, with this we can remove the environment.apiGithubNews
from our GithubNewsService
and we'll only have one place to change the environment.apiGithubNews
, if this ever happens in the future this will change!
This makes our code a little cleaner and easier to apply future changes.
Now, let's work!
🧩 Structuring the Solution
Let's see and apply the structure for the solution.
➾ Generate the HTTPInterceptor file:
First, let's generate the HTTPInterceptor
, with Angular CLI we just need put the generate command to generate the file, like below:
ng generate interceptor interceptors/set-domain
➾ Importing the HTTPInterceptor file:
Let's import the our HTTPInterceptor
in our app.module.ts
inside the providers
, so basically the structure will be:
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
import { SetDomainInterceptor } from './interceptors/set-domain.interceptor';
...
@NgModule({
....
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: SetDomainInterceptor,
multi: true,
},
],
...
})
export class AppModule { }
➾ Structuring the SetDomainInterceptor
:
As we saw above, we need to take the request flow, apply the logic we want and return the update request with next.handle()
, so basically the structure will be like below:
import { Injectable } from '@angular/core';
import {
HttpRequest,
HttpHandler,
HttpEvent,
HttpInterceptor
} from '@angular/common/http';
import { Observable } from 'rxjs';
import { environment } from '../../environments/environment';
@Injectable()
export class SetDomainInterceptor implements HttpInterceptor {
constructor() {}
intercept(httpRequest: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
const requestStructureUpdated: HttpRequest<any> = httpRequest
.clone({
url: `${ environment.urlApi }/${ httpRequest.url }`,
setHeaders: {
'Content-Type': 'application/json'
}
});
return next.handle(requestStructureUpdated);
}
}
Basically, we take the request and create a clone of it, passing the API url with environment.apiGithubNews
, after that we concatenate the rest of the request URL with ${ httpRequest.url }
and we also apply the 'Content-Type': 'application /json'
.
Now in our service structure, we can remove the environment.apiGithubNews
and have just the API url path, so the new structure will look like this:
...
export class GithubNewsService {
constructor(
private _httpClient: HttpClient
) { }
getGithubNews(): Observable<GithubNews[]> {
return this._httpClient
.get<GithubNews[]>('getNews');
}
saveNewGithubNews(news: GithubNews): Observable<GithubNews[]> {
return this._httpClient
.post<GithubNews>('saveNews', news);
}
}
Really cool and interesting, right?!
Now with this we can apply and passa whatever we want in all requests in our application, having only one place to do this and keeping our code easy to apply future changes.
We have many possibilities and I hope that with this new possibility you learn something and help you!
I hope you enjoyed today's article!
If know of any other way, have any comments or anything else, please leave a comment and let's talk!
See you soon 😁😁🤘🤘🤘
Top comments (9)
An alternative to cloning the request would be using an HttpClientFactory with the API URL as an argument. It was interesting seeing an example with HttpInterceptor though!
Wow, the way you commented too is very interesting! I'll try to understand better and apply it to have another way! Thanks ✌️
Renan Ferro,
Great article !
Thanks for sharing...
Muito obrigado João, fico feliz que tenha gostado 😄
Nice idea of use the base URL constant inside the Interceptor!
Eaee man
Bem legal né?! Facilita bastante 😆
Haha, interceptors are really great, once you wrap your head around them, you can begin to appreciate the design pattern.
Yes, things get more interesting each time 🤣
Still thinking their environment files are good for this. If you need to change the baseUrl -> just change the value (per env). Interceptors I would use for something like tokens or something like that. This we don't need to have in services, but we can inject this type of stuff in the interceptors.
Some comments have been hidden by the post's author - find out more