Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular - How to add an access token from an observable to an outgoing HTTP request?

in my UserService I've got a user Observable object which holds the UserModel of the user logged in. For testing I've implemented inside the ngOnInit() the login process:

this.userService.authenticate('###', '###')
   .subscribe(res => console.log('authenticated'));
private userSource = new BehaviorSubject<UserModel>(null);
   public user = this.userSource.asObservable();

My UserModel provides an attribtue called authKey which is used for the API auth.

In my ProjectService I would like to do an api request; in order to do this, the api key stored in the UserModel is necessary. It would possible just to subscribe the user attribute, but I read about avoiding to do subscriptions inside services.

Question

How can I connect this subscriptions with pipes/mapping? My approach was the following code; but that feels like bad code.

suggest(term: string): Observable<ProjectModel[]> {
      return this.userSrv.user.pipe(
         mergeMap((user: UserModel) => {
            const options = {params: {'access-token': user.accessToken}};

            return this.http.get<ProjectModel[]>(this.conf.url, options).pipe(
               map(response => {
                  // mapping the projects ...
                  return projects;
               })
            );
         })
      );
   }
like image 232
Lairg Avatar asked Oct 27 '22 17:10

Lairg


1 Answers

As already stated in the previous answer, a HTTP Interceptor is best suited for your use case.

The basic idea would look like this:

import {Injectable} from '@angular/core';
import {HttpEvent, HttpInterceptor, HttpHandler, HttpRequest} from '@angular/common/http';
import {mergeMap,map} from 'rxjs/operators';
import {Observable} from 'rxjs';

function isLoginRequest(req: HttpRequest<any>): boolean {
    // implement
}

@Injectable()
export class AccessTokenInterceptor implements HttpInterceptor {
  constructor(private readonly userService: UserService){}

  intercept(req: HttpRequest<any>, next: HttpHandler):
    Observable<HttpEvent<any>> {
    if(isLoginRequest(req)) return next.handle(req);
    return this.userService.user.pipe(
      map(user => req.clone({setParams:{'access-token': user.accessToken}})),
      mergeMap(req => next.handle(req))
    );
  }
}

Dont forget to register this interceptor as exemplified in the docs.

By using this, your original service function is reduced to:

suggest(term: string): Observable<ProjectModel[]> {
  return this.http.get<ProjectModel[]>(this.conf.url).pipe(
           map(response => {
              // mapping the projects ...
           })
        );
}
like image 192
Jota.Toledo Avatar answered Nov 16 '22 13:11

Jota.Toledo